diff --git a/.gitignore b/.gitignore index a7cc8803..c551f7a2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,10 @@ !client/hardnested/tables/*.z usb_cmd.lua version.c +armsrc/fpga_version_info.c client/ui/ui_overlays.h *.Td +.DS_Store *.exe hardnested_stats.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index f34b78f0..7781935e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,21 +2,73 @@ 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 -- Improved backdoor detection missbehaving magic s50/1k tag (Fl0-0) +- Changed hf mfp security. Now it works in all the modes. (drHatson) ### Fixed ### Added +- `hf mfp` group of commands (Merlok) + +## [v3.1.0][2018-10-10] + +### Changed +- Adjusted `lf cmdread` to respond to client when complete and the client will then automatically call `data samples` +- Improved backdoor detection misbehaving magic s50/1k tag (Fl0-0) +- Deleted wipe functionality from `hf mf csetuid` (Merlok) +- Changed `hf mf nested` logic (Merlok) +- Added `hf mf nested` mode: autosearch keys for attack (from well known keys) (Merlok) +- `hf mf nested` Check keys after they have found (Merlok) +- `hf mf chk` Move main cycle to arm (Merlok) +- Changed proxmark command line parameter `flush` to `-f` or `-flush` (Merlok) +- Changed `hf 14a reader` to just request-anticolission-select sequence (Merlok) +- Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) +- Changed TLV parser messages to more convenient (Merlok) +- Rewritten Legic Prime reader (`hf legic reader`, `write` and `fill`) - it is using xcorrelation now (AntiCat) +- `hf 14a` commands works via argtable3 commandline parsing library (Merlok) +- HID LF operations on firmware updated for complete native support of long (>37 bit) HID tags (grauerfuchs) +- Changed Legic Prime tag simulator (`hf legic sim`) to run from 212 kHz SSP clock for better reliability (AntiCat) + +### Fixed +- Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) +- Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) +- Changed all command line parsers in `hf emv` commands to argtable (Merlok) +- Implemented AppNap API, fixing #283 and #627 OSX USB comm issues (AntiCat) + +### Added +- Added `sc` smartcard (contact card) commands - reader, info, raw, upgrade, setclock, list (hardware version RDV4.0 only) must turn option on in makefile options (Willok, Iceman, marshmellow) +- Added a bitbang mode to `lf cmdread` if delay is 0 the cmd bits turn off and on the antenna with 0 and 1 respectively (marshmellow) - Added PAC/Stanley detection to lf search (marshmellow) - Added lf pac demod and lf pac read - extracts the raw blocks from a PAC/Stanley tag (marshmellow) -- Added hf mf c* commands compatibity for 4k and gen1b backdoor (Fl0-0) +- Added hf mf c* commands compatibility for 4k and gen1b backdoor (Fl0-0) - Added backdoor detection for gen1b magic s70/4k tag (Fl0-0) - Added data fsktonrz, a fsk cleaning/demodulating routine for weak fsk signal. Note: follow this up with a `data rawdemod nr` to finish demoding your signal. (marshmellow) - Added lf em 410xbrute, LF EM410x reader bruteforce attack by simulating UIDs from a file (Fl0-0) +- Added `hf mf cwipe` command. It wipes "magic Chinese" card. For 1a generation it uses card's "wipe" command. For gen1a and gen1b it uses a write command. (Merlok) +- Added to `hf mf nested` source key check before attack (Merlok) +- Added to `hf mf nested` after attack it checks all found keys on non-open sectors (Merlok) +- `hf mf chk` Added settings to set iso14443a operations timeout. default timeout set to 500us (Merlok) +- Added to `hf mf nested` parameters `s` and `ss` for checking slow cards (Merlok) +- Added to proxmark command line parameters `w` - wait 20s for serial port (Merlok) +- Added to proxmark command line parameters `c` and `l` - execute command and lua script from command line (Merlok) +- Added to proxmark ability to execute commands from stdin (pipe) (Merlok) +- Added `hf 14a info` and moved there functionality from `hf 14a reader` (Merlok) +- Added to `hf 14a info` detection of weak prng from Iceman1001 fork (Merlok) +- Added to `hf 14a apdu` - exchange apdu via iso1443-4 (Merlok) +- Added to `hf 14a apdu` - apdu and tlv results parser (Merlok) +- Added `hf emv` group of commands (Merlok) +- Added `hf emv search` `hf emv pse` - commands for selection of EMV application (Merlok) +- Added `hf emv select` - command for select EMV application (Merlok) +- Added `hf emv exec` - command for execute EMV transaction (Merlok) +- Added to `hf emv exec` MSD path for VISA and Mastercard and some other compatible EMV cards (Merlok) +- Added to `hf emv exec` SDA, DDA, fast DDA, CDA calculations for VISA and Mastercard and some other compatible EMV cards (Merlok) +- Added `hf emv test` - crypto tests for DES, AES, SHA, RSA, SDA, DDA, CDA and some other crypto functions (Merlok) +- Added `hf list mf` - deciphers crypto1 stream and works with first authentication and weak nested authentications (Merlok) +- Added to `hf emv` commands: `gpo`, `readrec`, `genac`, `challenge`, `intauth` - commands working with EMV cards (Merlok) +- Added `lf hid encode` and `lf hid decode` commands to translate printed HID card data to and from the packed data transmitted by a prox tag (grauerfuchs) +- Added `lf hid write` command, which operates as a macro for encode followed by clone operations (grauerfuchs) ## [3.0.1][2017-06-08] @@ -32,7 +84,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added lf hitag write 24, the command writes a block to hitag2 tags in crypto mode (henjo) ### Added -- Added hf mf hardnested, an attack working for hardened Mifare cards (EV1, Mifare Plus SL1) where hf mf nested fails +- Added hf mf hardnested, an attack working for hardened Mifare cards (EV1, Mifare Plus SL1) where hf mf nested fails (piwi) - Added experimental testmode write option for t55xx (danger) (marshmellow) - Added t55xx p1detect to `lf search` chip detections (marshmellow) - Added lf t55xx p1detect, detect page 1 of a t55xx tag based on E015 mfg code (marshmellow) @@ -194,5 +246,3 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ### Added - iClass functionality: full simulation of iclass tags, so tags can be simulated with data (not only CSN). Not yet support for write/update, but readers don't seem to enforce update. (holiman). - iClass decryption. Proxmark can now decrypt data on an iclass tag, but requires you to have the HID decryption key locally on your computer, as this is not bundled with the sourcecode. - - diff --git a/CI/.travis.yml b/CI/.travis.yml new file mode 100644 index 00000000..f5fed7a6 --- /dev/null +++ b/CI/.travis.yml @@ -0,0 +1,55 @@ +# Travis-CI config +# variable REPOSITORY_EP must be filled with repository name. as sample: "merlokk/proxmark3" +language: c + +compiler: gcc + +# Test on Linux and MacOS +matrix: + include: + - os: osx + osx_image: xcode7.3 # OS X 10.11 + - os: osx + osx_image: xcode8.3 # OS X 10.12 + - os: osx + osx_image: xcode9.4 # OS X 10.13 + - os: osx + osx_image: xcode10 # OS X 10.13 + - os: linux + dist: trusty + sudo: required + +before_install: +## Install ARM toolchain on Linux. +## add our homebrew tap for MacOS +## Note: all dependencies on MacOS should be resolved by the brew install command + echo $REPOSITORY_EP; + if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + sudo apt-get update -qq; + sudo apt-get install -y gcc-arm-none-eabi; + elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew update; + if [[ "$REPOSITORY_EP" == "" ]]; then + brew tap proxmark/proxmark3; + else + brew tap "$REPOSITORY_EP" --env=std; + fi + fi + +install: + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + brew info proxmark3; + brew install -v --HEAD proxmark3; + elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + make all; + fi + +before_script: + +script: +## for the time being we are satisfied if it can be build and then successfully started + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + proxmark3 /dev/notexists travis_test_commands.scr ; + elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + ./client/proxmark3 /dev/notexists travis_test_commands.scr ; + fi diff --git a/CI/appveyor.yml b/CI/appveyor.yml new file mode 100644 index 00000000..a38ff647 --- /dev/null +++ b/CI/appveyor.yml @@ -0,0 +1,321 @@ +version: 3.0.1.{build} +image: Visual Studio 2017 +clone_folder: C:\ProxSpace\pm3 +init: +- ps: >- + $psversiontable + + #Get-ChildItem Env: + + + $releasename="" + + $env:APPVEYOR_REPO_COMMIT_SHORT = $env:APPVEYOR_REPO_COMMIT.Substring(0, 8) + + if ($env:appveyor_repo_tag -match "true"){ + $releasename=$env:APPVEYOR_REPO_TAG_NAME + "/" + } + + $releasename+=$env:APPVEYOR_BUILD_VERSION + " [" + $env:APPVEYOR_REPO_COMMIT_SHORT + "]" + + + Write-Host "repository: $env:appveyor_repo_name branch:$env:APPVEYOR_REPO_BRANCH release: $releasename" -ForegroundColor Yellow + + Add-AppveyorMessage -Message "[$env:APPVEYOR_REPO_COMMIT_SHORT]$env:appveyor_repo_name($env:APPVEYOR_REPO_BRANCH)" -Category Information -Details "repository: $env:appveyor_repo_name branch: $env:APPVEYOR_REPO_BRANCH release: $releasename" + + + iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) +clone_script: +- ps: >- + Write-Host "Removing ProxSpace..." -NoNewLine + + cd \ + + Remove-Item -Recurse -Force -Path c:\ProxSpace\* + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Git clone ProxSpace..." -NoNewLine + + git clone -q https://github.com/Gator96100/ProxSpace c:\ProxSpace + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Removing pm3 dir..." -NoNewLine + + Remove-Item -Recurse -Force -Path c:\ProxSpace\pm3\* + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Cloning repository <$env:appveyor_repo_name> to $env:appveyor_build_folder ..." -NoNewLine + + if(-not $env:appveyor_pull_request_number) { + git clone -q --branch=$env:appveyor_repo_branch https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder + cd $env:appveyor_build_folder + git checkout -qf $env:appveyor_repo_commit + } else { + git clone -q https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder + cd $env:appveyor_build_folder + git fetch -q origin +refs/pull/$env:appveyor_pull_request_number/merge: + git checkout -qf FETCH_HEAD + } + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Fill msys2\etc\fstab file..." -NoNewLine + + New-Item c:\ProxSpace\msys2\etc\fstab -type file -force -value "# For a description of the file format, see the Users Guide`n# http://cygwin.com/cygwin-ug-net/using.html#mount-table`nnone / cygdrive binary,posix=0,noacl,user 0 0 `nC:\ProxSpace\pm3 /pm3 ntfs noacl 0 0 `nC:\ProxSpace\gcc-arm-none-eabi /gcc-arm-none-eabi ntfs noacl 0 0 `n" + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Update msys2 packages..." -NoNewLine + + $env:Path = "C:\ProxSpace\msys2\usr\bin;C:\ProxSpace\msys2\mingw32\bin;C:\ProxSpace\gcc-arm-none-eabi\bin;$env:Path" + + C:\ProxSpace\msys2\msys2_shell.cmd -mingw32 -defterm -no-start /dev/null 1> msys1.txt 2>&1 + + C:\ProxSpace\msys2\msys2_shell.cmd -mingw32 -defterm -no-start /dev/null 1> msys1.txt 2>&1 + + Write-Host "[ OK ]" -ForegroundColor Green +install: +- ps: >- + function Exec-External { + param( + [Parameter(Position=0,Mandatory=1)][scriptblock] $command + ) + & $command + if ($LASTEXITCODE -ne 0) { + throw ("Command returned non-zero error-code ${LASTEXITCODE}: $command") + } + } +build_script: +- ps: >- + "C:\ProxSpace\msys2\usr\bin;C:\ProxSpace\msys2\mingw32\bin;C:\ProxSpace\gcc-arm-none-eabi\bin;$env:Path" + + + $env:MINGW_HOME="C:\ProxSpace\msys2\mingw32" + + $env:MSYS_HOME="C:\ProxSpace\msys2" + + $env:MSYSTEM="MINGW32" + + $env:MINGW_PREFIX="/mingw32" + + $env:SHELL="/bin/bash" + + $env:MSYSTEM_CHOST="i686-w64-mingw32" + + + #make + + bash -c -i 'pwd;make clean;make all' + + + #some checks + + if(!(Test-Path C:\ProxSpace\pm3\client\proxmark3.exe)){ + + throw "Main file proxmark3.exe not exists." + + } + + if(!(Test-Path C:\ProxSpace\pm3\armsrc\obj\fullimage.elf)){ + + throw "ARM file fullimage.elf not exists." + + } + + if(!(Test-Path C:\ProxSpace\pm3\client\hardnested\tables\*.bin.z)){ + + throw "Files in hardnested\tables not exists." + + } + + + #copy + + Write-Host "Copy release files..." -NoNewLine -ForegroundColor Yellow + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release + + Copy-Item C:\ProxSpace\pm3\client\*.exe C:\ProxSpace\Release + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\arm + + Copy-Item C:\ProxSpace\pm3\armsrc\obj\*.elf C:\ProxSpace\Release\arm + + Copy-Item C:\ProxSpace\pm3\bootrom\obj\*.elf C:\ProxSpace\Release\arm + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\scripts + + Copy-Item C:\ProxSpace\pm3\client\scripts\*.lua C:\ProxSpace\Release\scripts + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\hardnested\tables + + Copy-Item C:\ProxSpace\pm3\client\hardnested\*.bin C:\ProxSpace\Release\hardnested + + Copy-Item C:\ProxSpace\pm3\client\hardnested\tables\*.bin.z C:\ProxSpace\Release\hardnested\tables + + Write-Host "[ OK ]" -ForegroundColor Green + + + #archive and push + + $releasename="" + + if ($env:appveyor_repo_tag -match "true"){ + + $releasename=$env:APPVEYOR_REPO_TAG_NAME + "/" + + } + + $releasename+=$env:APPVEYOR_BUILD_VERSION + " [" + $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) + "]" + + + Write-Host "Archive and publish release files ($releasename)..." -NoNewLine -ForegroundColor Yellow + + cd C:\ProxSpace + + 7z a release.zip C:\ProxSpace\Release + + Push-AppveyorArtifact release.zip -DeploymentName "$releasename" + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Builded..." -ForegroundColor Yellow +test_script: +- ps: >- + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + + cd c:\ProxSpace\pm3 + + + $global:TestsPassed=$true + + + Function ExecTest($Name, $File, $Cmd, $CheckResult) { + + #--- begin Job + + $Job = Start-Job -ScriptBlock { + [bool]$res=$false + $TestTime=[System.Environment]::TickCount + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + Set-Location $using:PWD + + $sb=[scriptblock]::Create("$using:Cmd") + #execute scriptblock + Write-host "Test [$using:Name] job: $using:Cmd" + $Cond=&$sb + + if ($Cond -eq $null){ + } ElseIf($using:CheckResult -ne $null) { + [String]$searchstr="" + if ($Cond -is [Object]){ + ForEach($line in $Cond){ + Write-host $line -ForegroundColor Gray + $searchstr += $line + } + }else{ + Write-host "$Cond" -ForegroundColor Gray + $searchstr = $Cond + } + If($searchstr -like "*$using:CheckResult*") { + $res=$true + } + $Cond="*$using:CheckResult*" + } Else { + If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){ + if ($Cond -is "String" -and $Cond -like "*passed*"){ + $res= $true + } + if ($Cond -is "String" -and $Cond -like "*true*"){ + $res= $true + } + } Else { + $res=$Cond + } + } + + If ($res) { + Write-host "Result[$using:Name]: $Cond" -ForegroundColor Green + Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" + }Else { + Write-host "Result[$using:Name]: $Cond" -ForegroundColor Red + Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" -ErrorMessage "command:$using:Cmd`nresult:$Cond" + } + return $res + } + + #--- end Job + + [bool]$res=$false + # Wait 120 sec timeout for Job + if(Wait-Job $Job -Timeout 150){ + $Results = $Job | Receive-Job + if($Results -like "true"){ + $res=$true + } + } else { + Write-host "Test [$Name] timeout" -ForegroundColor Red + Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration 60000 -ErrorMessage "timeout" + } + Remove-Job -Force $Job + + if(!$res){ + $global:TestsPassed=$false + } + } + + + Write-Host "Running tests..." -ForegroundColor Yellow + + + #file test + + ExecTest "proxmark3 exists" "proxmark3.exe" {Test-Path C:\ProxSpace\Release\proxmark3.exe} + + ExecTest "arm image exists" "\arm\fullimage1.elf" {Test-Path C:\ProxSpace\Release\arm\fullimage.elf} + + ExecTest "bootrom exists" "bootrom.elf" {Test-Path C:\ProxSpace\Release\arm\bootrom.elf} + + ExecTest "hardnested tables exists" "hardnested" {Test-Path C:\ProxSpace\Release\hardnested\tables\*.z} + + ExecTest "release exists" "release.zip" {Test-Path C:\ProxSpace\release.zip} + + + #proxmark logic tests + + ExecTest "proxmark help" "proxmark3 -h" {bash -lc 'cd ~/client;./proxmark3 -h | grep -q Execute && echo Passed || echo Failed'} + + ExecTest "proxmark help hardnested" "proxmark3 -h" {bash -lc 'cd ~/client;./proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'} + + + ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;./proxmark3 comx -c 'hf mf'"} "at_enc" + + ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;./proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:" + + + #proxmark crypto tests + + ExecTest "hf emv test" "hf emv test" {bash -lc "cd ~/client;./proxmark3 comx -c 'hf emv test'"} "Tests ?OK" + + + if ($global:TestsPassed) { + Write-Host "Tests [ OK ]" -ForegroundColor Green + } else { + Write-Host "Tests [ ERROR ]" -ForegroundColor Red + throw "Tests error." + } +on_success: +- ps: Write-Host "Build success..." -ForegroundColor Green +on_failure: +- ps: Write-Host "Build error." -ForegroundColor Red +on_finish: +- ps: $blockRdp = $false; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/CI/readme.md b/CI/readme.md new file mode 100644 index 00000000..d4159290 --- /dev/null +++ b/CI/readme.md @@ -0,0 +1,20 @@ +# How to configure continuous integration + +Here 2 CI configuration files: + +1. for [travis](travis-ci.org) +2. for [appveyor](appveyor.com) + +It needs to put files from this directory to repository root and then configure CI from appropriate WEB portal. + +## travis + +- Copy .travis.yml and travis_test_commands.scr files to repository root +- Configure CI from http://travis-ci.org +- It needs to fork https://github.com/Proxmark/homebrew-proxmark3 from your proxmark repository home +- Put to file `proxmark3.rb` in line `head "https://github.com/proxmark/proxmark3.git"` your repository link. As sample: `head "https://github.com/merlokk/proxmark3.git"` + + +## appveyor + +- Just copy appveyor.yml file to root and configure it from http://appveyor.com diff --git a/CI/travis_test_commands.scr b/CI/travis_test_commands.scr new file mode 100644 index 00000000..4f5b025c --- /dev/null +++ b/CI/travis_test_commands.scr @@ -0,0 +1,3 @@ +hf mf hardnested t 1 000000000000 +hf emv test +exit diff --git a/README.txt b/README.md similarity index 66% rename from README.txt rename to README.md index c8cac497..09f23a6b 100644 --- a/README.txt +++ b/README.md @@ -1,56 +1,38 @@ -NOTICE: -(2014-03-26) -This is now the official Proxmark repository! - -INTRODUCTION: +# proxmark3: the official Proxmark repository! The proxmark3 is a powerful general purpose RFID tool, the size of a deck of cards, designed to snoop, listen and emulate everything from -Low Frequency (125kHz) to High Frequency (13.56MHz) tags. +**Low Frequency (125kHz)** to **High Frequency (13.56MHz)** tags. This repository contains enough software, logic (for the FPGA), and design documentation for the hardware that you could, at least in theory, do something useful with a proxmark3. -RESOURCES: +## Resources - * This repository! - https://github.com/Proxmark/proxmark3 - - * The Wiki - https://github.com/Proxmark/proxmark3/wiki - - * The GitHub page - http://proxmark.github.io/proxmark3/ - - * The Forum - http://www.proxmark.org/forum - - * The IRC chanel - irc.freenode.org #proxmark3 - -or- - http://webchat.freenode.net/?channels=#proxmark3 - - * The Homebrew formula repository - https://github.com/Proxmark/homebrew-proxmark3 +* [This repository!](https://github.com/Proxmark/proxmark3) +* [The Wiki](https://github.com/Proxmark/proxmark3/wiki) +* [The GitHub Pages website](http://proxmark.github.io/proxmark3/) +* [The Forum](http://www.proxmark.org/forum) +* The IRC channel: irc.freenode.org #proxmark3 ([chat in your browser](http://webchat.freenode.net/?channels=#proxmark3)) +* [The Homebrew formula repository](https://github.com/Proxmark/homebrew-proxmark3) -DEVELOPMENT: +## Development The tools required to build or run the project will vary depending on -your operating system. Please refer to the Wiki for details. +your operating system. Please refer to [the wiki](https://github.com/Proxmark/proxmark3/wiki) for details. - * https://github.com/Proxmark/proxmark3/wiki - -OBTAINING HARDWARE: +## Obtaining hardware The Proxmark3 is available for purchase (assembled and tested) from the following locations: - * https://proxmark3.com/ - RyscCorp (us) - * http://www.elechouse.com/ - Elechouse (HK) - * https://lab401.com/ - Lab401 (FR) - * http://www.rfxsecure.com/ - RFxSecure (SG) - * http://proxmark3.tictail.com/ - IceSQL (SE) +* [RyscCorp](https://proxmark3.com/) (US) +* [Hackerwarehouse](https://hackerwarehouse.com/) (US) +* [Elechouse](http://www.elechouse.com/) (HK) +* [Lab401](https://lab401.com/) (FR) +* [RFxSecure](http://www.rfxsecure.com/) (SG) +* [IceSQL](http://proxmark3.tictail.com/) (SE) Most of the ultra-low-volume contract assemblers could put something like this together with a reasonable yield. A run of around @@ -70,7 +52,7 @@ The printed circuit board artwork is also available, as Gerbers and an Excellon drill file. -LICENSING: +## License 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 diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 8870f426..4fe97b46 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -136,6 +136,10 @@ void set_tracing(bool enable) { tracing = enable; } +bool get_tracing(void) { + return tracing; +} + /** * Get the number of bytes traced * @return diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 6a052dca..05538044 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -37,6 +37,7 @@ extern void BigBuf_print_status(void); extern uint16_t BigBuf_get_traceLen(void); extern void clear_trace(void); extern void set_tracing(bool enable); +extern bool get_tracing(void); extern bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); extern int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader); extern uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length); diff --git a/armsrc/Makefile b/armsrc/Makefile index 861d98c6..d230cda1 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -10,17 +10,23 @@ APP_INCLUDES = apps.h #remove one of the following defines and comment out the relevant line #in the next section to remove that particular feature from compilation -APP_CFLAGS = -DWITH_ISO14443a_StandAlone -DWITH_LF -DWITH_ISO15693 -DWITH_ISO14443a -DWITH_ISO14443b -DWITH_ICLASS -DWITH_LEGICRF -DWITH_HITAG -DWITH_CRC -DON_DEVICE -DWITH_HFSNOOP \ - -fno-strict-aliasing -ffunction-sections -fdata-sections -#-DWITH_LCD +APP_CFLAGS = -DON_DEVICE \ + -fno-strict-aliasing -ffunction-sections -fdata-sections -#SRC_LCD = fonts.c LCD.c +include ../common/Makefile_Enabled_Options.common + +ifneq (,$(findstring WITH_LCD,$(APP_CFLAGS))) + SRC_LCD = fonts.c LCD.c +else + SRC_LCD = +endif SRC_LF = lfops.c hitag2.c hitagS.c lfsampling.c pcf7931.c lfdemod.c protocols.c SRC_ISO15693 = iso15693.c iso15693tools.c -SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c +SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c mifaresim.c SRC_ISO14443b = iso14443b.c SRC_CRAPTO1 = crypto1.c des.c SRC_CRC = iso14443crc.c crc.c crc16.c crc32.c parity.c +SRC_SMARTCARD = i2c.c #the FPGA bitstream files. Note: order matters! FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit @@ -39,6 +45,7 @@ THUMBSRC = start.c \ $(SRC_ISO15693) \ $(SRC_LF) \ $(SRC_ZLIB) \ + $(SRC_SMARTCARD) \ appmain.c \ printf.c \ util.c \ @@ -49,16 +56,20 @@ THUMBSRC = start.c \ # These are to be compiled in ARM mode ARMSRC = fpgaloader.c \ legicrf.c \ + legicrfsim.c \ + legic_prng.c \ $(SRC_ISO14443a) \ $(SRC_ISO14443b) \ $(SRC_CRAPTO1) \ $(SRC_CRC) \ - legic_prng.c \ iclass.c \ BigBuf.c \ optimized_cipher.c \ hfsnoop.c +VERSIONSRC = version.c \ + fpga_version_info.c + # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC include ../common/Makefile.common @@ -69,6 +80,14 @@ all: $(OBJS) .DELETE_ON_ERROR: +# version.c should be remade on every compilation +.PHONY: version.c +version.c: default_version.c + perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ + +fpga_version_info.c: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) + $(FPGA_COMPRESSOR) -v $(filter %.bit,$^) $@ + $(OBJDIR)/fpga_all.o: $(OBJDIR)/fpga_all.bit.z $(OBJCOPY) -O elf32-littlearm -I binary -B arm --prefix-sections=fpga_all_bit $^ $@ diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 25285396..573a3a71 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -16,22 +16,26 @@ #include "cmd.h" #include "proxmark3.h" #include "apps.h" +#include "fpga.h" #include "util.h" #include "printf.h" #include "string.h" #include "legicrf.h" +#include "legicrfsim.h" #include "hitag2.h" #include "hitagS.h" #include "lfsampling.h" #include "BigBuf.h" #include "mifareutil.h" #include "pcf7931.h" +#include "i2c.h" #ifdef WITH_LCD #include "LCD.h" #endif + // Craig Young - 14a stand-alone code -#ifdef WITH_ISO14443a_StandAlone +#ifdef WITH_ISO14443a #include "iso14443a.h" #endif @@ -135,35 +139,28 @@ void Dbhexdump(int len, uint8_t *d, bool bAsci) { // return that. //----------------------------------------------------------------------------- static int ReadAdc(int ch) -{ - uint32_t d; - - AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; - AT91C_BASE_ADC->ADC_MR = - ADC_MODE_PRESCALE(63 /* was 32 */) | // ADC_CLK = MCK / ((63+1) * 2) = 48MHz / 128 = 375kHz - ADC_MODE_STARTUP_TIME(1 /* was 16 */) | // Startup Time = (1+1) * 8 / ADC_CLK = 16 / 375kHz = 42,7us Note: must be > 20us - ADC_MODE_SAMPLE_HOLD_TIME(15 /* was 8 */); // Sample & Hold Time SHTIM = 15 / ADC_CLK = 15 / 375kHz = 40us - +{ // Note: ADC_MODE_PRESCALE and ADC_MODE_SAMPLE_HOLD_TIME are set to the maximum allowed value. - // Both AMPL_LO and AMPL_HI are very high impedance (10MOhm) outputs, the input capacitance of the ADC is 12pF (typical). This results in a time constant - // of RC = 10MOhm * 12pF = 120us. Even after the maximum configurable sample&hold time of 40us the input capacitor will not be fully charged. + // AMPL_HI is a high impedance (10MOhm || 1MOhm) output, the input capacitance of the ADC is 12pF (typical). This results in a time constant + // of RC = (0.91MOhm) * 12pF = 10.9us. Even after the maximum configurable sample&hold time of 40us the input capacitor will not be fully charged. // // The maths are: // If there is a voltage v_in at the input, the voltage v_cap at the capacitor (this is what we are measuring) will be // - // v_cap = v_in * (1 - exp(-RC/SHTIM)) = v_in * (1 - exp(-3)) = v_in * 0,95 (i.e. an error of 5%) - // - // Note: with the "historic" values in the comments above, the error was 34% !!! - - AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ch); + // v_cap = v_in * (1 - exp(-SHTIM/RC)) = v_in * (1 - exp(-40us/10.9us)) = v_in * 0,97 (i.e. an error of 3%) + AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; + AT91C_BASE_ADC->ADC_MR = + ADC_MODE_PRESCALE(63) | // ADC_CLK = MCK / ((63+1) * 2) = 48MHz / 128 = 375kHz + ADC_MODE_STARTUP_TIME(1) | // Startup Time = (1+1) * 8 / ADC_CLK = 16 / 375kHz = 42,7us Note: must be > 20us + ADC_MODE_SAMPLE_HOLD_TIME(15); // Sample & Hold Time SHTIM = 15 / ADC_CLK = 15 / 375kHz = 40us + + AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ch); AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; - while(!(AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ch))) - ; - d = AT91C_BASE_ADC->ADC_CDR[ch]; - - return d; + while(!(AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ch))) {}; + + return AT91C_BASE_ADC->ADC_CDR[ch] & 0x3ff; } int AvgAdc(int ch) // was static - merlok @@ -178,6 +175,26 @@ int AvgAdc(int ch) // was static - merlok return (a + 15) >> 5; } +static int AvgAdc_Voltage_HF(void) +{ + int AvgAdc_Voltage_Low, AvgAdc_Voltage_High; + + AvgAdc_Voltage_Low= (MAX_ADC_HF_VOLTAGE_LOW * AvgAdc(ADC_CHAN_HF_LOW)) >> 10; + // if voltage range is about to be exceeded, use high voltage ADC channel if available (RDV40 only) + if (AvgAdc_Voltage_Low > MAX_ADC_HF_VOLTAGE_LOW - 300) { + AvgAdc_Voltage_High = (MAX_ADC_HF_VOLTAGE_HIGH * AvgAdc(ADC_CHAN_HF_HIGH)) >> 10; + if (AvgAdc_Voltage_High >= AvgAdc_Voltage_Low) { + return AvgAdc_Voltage_High; + } + } + return AvgAdc_Voltage_Low; +} + +static int AvgAdc_Voltage_LF(void) +{ + return (MAX_ADC_LF_VOLTAGE * AvgAdc(ADC_CHAN_LF)) >> 10; +} + void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv, uint8_t LF_Results[]) { int i, adcval = 0, peak = 0; @@ -193,15 +210,17 @@ void MeasureAntennaTuningLfOnly(int *vLf125, int *vLf134, int *peakf, int *peakv FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + SpinDelay(50); + for (i=255; i>=19; i--) { WDT_HIT(); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, i); SpinDelay(20); - adcval = ((MAX_ADC_LF_VOLTAGE * AvgAdc(ADC_CHAN_LF)) >> 10); + adcval = AvgAdc_Voltage_LF(); if (i==95) *vLf125 = adcval; // voltage at 125Khz if (i==89) *vLf134 = adcval; // voltage at 134Khz - LF_Results[i] = adcval>>8; // scale int to fit in byte for graphing purposes + LF_Results[i] = adcval >> 9; // scale int to fit in byte for graphing purposes if(LF_Results[i] > peak) { *peakv = adcval; peak = LF_Results[i]; @@ -222,9 +241,8 @@ void MeasureAntennaTuningHfOnly(int *vHf) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(20); - *vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; + *vHf = AvgAdc_Voltage_HF(); LED_A_OFF(); - return; } @@ -249,7 +267,7 @@ void MeasureAntennaTuning(int mode) } } - cmd_send(CMD_MEASURED_ANTENNA_TUNING, vLf125 | (vLf134<<16), vHf, peakf | (peakv<<16), LF_Results, 256); + cmd_send(CMD_MEASURED_ANTENNA_TUNING, vLf125>>1 | (vLf134>>1<<16), vHf, peakf | (peakv>>1<<16), LF_Results, 256); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_B_OFF(); return; @@ -266,8 +284,8 @@ void MeasureAntennaTuningHf(void) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); for (;;) { - SpinDelay(20); - vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; + SpinDelay(500); + vHf = AvgAdc_Voltage_HF(); Dbprintf("%d mV",vHf); if (BUTTON_PRESS()) break; @@ -291,6 +309,8 @@ void ReadMem(int addr) extern struct version_information version_information; /* bootrom version information is pointed to from _bootphase1_version_pointer */ extern char *_bootphase1_version_pointer, _flash_start, _flash_end, _bootrom_start, _bootrom_end, __data_src_start__; + + void SendVersion(void) { char temp[USB_CMD_DATA_SIZE]; /* Limited data payload in USB packets */ @@ -311,11 +331,18 @@ void SendVersion(void) FormatVersionInformation(temp, sizeof(temp), "os: ", &version_information); strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); - FpgaGatherVersion(FPGA_BITSTREAM_LF, temp, sizeof(temp)); - strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); - FpgaGatherVersion(FPGA_BITSTREAM_HF, temp, sizeof(temp)); - strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); - + for (int i = 0; i < fpga_bitstream_num; i++) { + strncat(VersionString, fpga_version_information[i], sizeof(VersionString) - strlen(VersionString) - 1); + strncat(VersionString, "\n", sizeof(VersionString) - strlen(VersionString) - 1); + } + + // test availability of SmartCard slot + if (I2C_is_available()) { + strncat(VersionString, "SmartCard Slot: available\n", sizeof(VersionString) - strlen(VersionString) - 1); + } else { + strncat(VersionString, "SmartCard Slot: not available\n", sizeof(VersionString) - strlen(VersionString) - 1); + } + // Send Chip ID and used flash memory uint32_t text_and_rodata_section_size = (uint32_t)&__data_src_start__ - (uint32_t)&_flash_start; uint32_t compressed_data_section_size = common_area.arg1; @@ -358,17 +385,20 @@ void SendStatus(void) { BigBuf_print_status(); Fpga_print_status(); +#ifdef WITH_SMARTCARD + I2C_print_status(); +#endif printConfig(); //LF Sampling config printUSBSpeed(); Dbprintf("Various"); - Dbprintf(" MF_DBGLEVEL......%d", MF_DBGLEVEL); - Dbprintf(" ToSendMax........%d",ToSendMax); - Dbprintf(" ToSendBit........%d",ToSendBit); + Dbprintf(" MF_DBGLEVEL........%d", MF_DBGLEVEL); + Dbprintf(" ToSendMax..........%d", ToSendMax); + Dbprintf(" ToSendBit..........%d", ToSendBit); cmd_send(CMD_ACK,1,0,0,0,0); } -#if defined(WITH_ISO14443a_StandAlone) || defined(WITH_LF) +#if defined(WITH_ISO14443a_StandAlone) || defined(WITH_LF_StandAlone) #define OPTS 2 @@ -399,8 +429,8 @@ void StandAloneMode14a() FpgaDownloadAndGo(FPGA_BITSTREAM_HF); int selected = 0; - int playing = 0, iGotoRecord = 0, iGotoClone = 0; - int cardRead[OPTS] = {0}; + bool playing = false, GotoRecord = false, GotoClone = false; + bool cardRead[OPTS] = {false}; uint8_t readUID[10] = {0}; uint32_t uid_1st[OPTS]={0}; uint32_t uid_2nd[OPTS]={0}; @@ -416,9 +446,9 @@ void StandAloneMode14a() WDT_HIT(); SpinDelay(300); - if (iGotoRecord == 1 || cardRead[selected] == 0) + if (GotoRecord || !cardRead[selected]) { - iGotoRecord = 0; + GotoRecord = false; LEDsoff(); LED(selected + 1, 0); LED(LED_RED2, 0); @@ -443,14 +473,14 @@ void StandAloneMode14a() else if (cardRead[(selected+1)%OPTS]) { Dbprintf("Button press detected but no card in bank[%d] so playing from bank[%d]", selected, (selected+1)%OPTS); selected = (selected+1)%OPTS; - break; // playing = 1; + break; } else { Dbprintf("Button press detected but no stored tag to play. (Ignoring button)"); SpinDelay(300); } } - if (!iso14443a_select_card(uid, &hi14a_card[selected], &cuid, true, 0)) + if (!iso14443a_select_card(uid, &hi14a_card[selected], &cuid, true, 0, true)) continue; else { @@ -493,14 +523,14 @@ void StandAloneMode14a() LED(selected + 1, 0); // Next state is replay: - playing = 1; + playing = true; - cardRead[selected] = 1; + cardRead[selected] = true; } /* MF Classic UID clone */ - else if (iGotoClone==1) + else if (GotoClone) { - iGotoClone=0; + GotoClone=false; LEDsoff(); LED(selected + 1, 0); LED(LED_ORANGE, 250); @@ -551,7 +581,7 @@ void StandAloneMode14a() MifareCGetBlock(0x3F, 1, 0, oldBlock0); if (oldBlock0[0] == 0 && oldBlock0[0] == oldBlock0[1] && oldBlock0[1] == oldBlock0[2] && oldBlock0[2] == oldBlock0[3]) { Dbprintf("No changeable tag detected. Returning to replay mode for bank[%d]", selected); - playing = 1; + playing = true; } else { Dbprintf("UID from target tag: %02X%02X%02X%02X", oldBlock0[0],oldBlock0[1],oldBlock0[2],oldBlock0[3]); @@ -569,14 +599,14 @@ void StandAloneMode14a() if (memcmp(testBlock0,newBlock0,16)==0) { DbpString("Cloned successfull!"); - cardRead[selected] = 0; // Only if the card was cloned successfully should we clear it - playing = 0; - iGotoRecord = 1; + cardRead[selected] = false; // Only if the card was cloned successfully should we clear it + playing = false; + GotoRecord = true; selected = (selected+1) % OPTS; } else { Dbprintf("Clone failed. Back to replay mode on bank[%d]", selected); - playing = 1; + playing = true; } } LEDsoff(); @@ -584,72 +614,66 @@ void StandAloneMode14a() } // Change where to record (or begin playing) - else if (playing==1) // button_pressed == BUTTON_SINGLE_CLICK && cardRead[selected]) + else if (playing) // button_pressed == BUTTON_SINGLE_CLICK && cardRead[selected]) { LEDsoff(); LED(selected + 1, 0); // Begin transmitting - if (playing) - { - LED(LED_GREEN, 0); - DbpString("Playing"); - for ( ; ; ) { - WDT_HIT(); - int button_action = BUTTON_HELD(1000); - if (button_action == 0) { // No button action, proceed with sim - uint8_t data[512] = {0}; // in case there is a read command received we shouldn't break - Dbprintf("Simulating ISO14443a tag with uid[0]: %08x, uid[1]: %08x [Bank: %u]", uid_1st[selected],uid_2nd[selected],selected); - if (hi14a_card[selected].sak == 8 && hi14a_card[selected].atqa[0] == 4 && hi14a_card[selected].atqa[1] == 0) { - DbpString("Mifare Classic"); - SimulateIso14443aTag(1,uid_1st[selected], uid_2nd[selected], data); // Mifare Classic - } - else if (hi14a_card[selected].sak == 0 && hi14a_card[selected].atqa[0] == 0x44 && hi14a_card[selected].atqa[1] == 0) { - DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2,uid_1st[selected],uid_2nd[selected],data); // Mifare Ultralight - } - else if (hi14a_card[selected].sak == 20 && hi14a_card[selected].atqa[0] == 0x44 && hi14a_card[selected].atqa[1] == 3) { - DbpString("Mifare DESFire"); - SimulateIso14443aTag(3,uid_1st[selected],uid_2nd[selected],data); // Mifare DESFire - } - else { - Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1,uid_1st[selected], uid_2nd[selected], data); - } + LED(LED_GREEN, 0); + DbpString("Playing"); + for ( ; ; ) { + WDT_HIT(); + int button_action = BUTTON_HELD(1000); + if (button_action == 0) { // No button action, proceed with sim + uint8_t data[512] = {0}; // in case there is a read command received we shouldn't break + Dbprintf("Simulating ISO14443a tag with uid[0]: %08x, uid[1]: %08x [Bank: %u]", uid_1st[selected],uid_2nd[selected],selected); + if (hi14a_card[selected].sak == 8 && hi14a_card[selected].atqa[0] == 4 && hi14a_card[selected].atqa[1] == 0) { + DbpString("Mifare Classic"); + SimulateIso14443aTag(1,uid_1st[selected], uid_2nd[selected], data); // Mifare Classic } - else if (button_action == BUTTON_SINGLE_CLICK) { - selected = (selected + 1) % OPTS; - Dbprintf("Done playing. Switching to record mode on bank %d",selected); - iGotoRecord = 1; - break; + else if (hi14a_card[selected].sak == 0 && hi14a_card[selected].atqa[0] == 0x44 && hi14a_card[selected].atqa[1] == 0) { + DbpString("Mifare Ultralight"); + SimulateIso14443aTag(2,uid_1st[selected],uid_2nd[selected],data); // Mifare Ultralight } - else if (button_action == BUTTON_HOLD) { - Dbprintf("Playtime over. Begin cloning..."); - iGotoClone = 1; - break; + else if (hi14a_card[selected].sak == 20 && hi14a_card[selected].atqa[0] == 0x44 && hi14a_card[selected].atqa[1] == 3) { + DbpString("Mifare DESFire"); + SimulateIso14443aTag(3,uid_1st[selected],uid_2nd[selected],data); // Mifare DESFire + } + else { + Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); + SimulateIso14443aTag(1,uid_1st[selected], uid_2nd[selected], data); } - WDT_HIT(); } - - /* We pressed a button so ignore it here with a delay */ - SpinDelay(300); - LEDsoff(); - LED(selected + 1, 0); + else if (button_action == BUTTON_SINGLE_CLICK) { + selected = (selected + 1) % OPTS; + Dbprintf("Done playing. Switching to record mode on bank %d",selected); + GotoRecord = true; + break; + } + else if (button_action == BUTTON_HOLD) { + Dbprintf("Playtime over. Begin cloning..."); + GotoClone = true; + break; + } + WDT_HIT(); } - else - while(BUTTON_PRESS()) - WDT_HIT(); + + /* We pressed a button so ignore it here with a delay */ + SpinDelay(300); + LEDsoff(); + LED(selected + 1, 0); } } } -#elif WITH_LF +#elif WITH_LF_StandAlone // samy's sniff and repeat routine void SamyRun() { StandAloneMode(); FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - int high[OPTS], low[OPTS]; + int tops[OPTS], high[OPTS], low[OPTS]; int selected = 0; int playing = 0; int cardRead = 0; @@ -683,8 +707,11 @@ void SamyRun() /* need this delay to prevent catching some weird data */ SpinDelay(500); - CmdHIDdemodFSK(1, &high[selected], &low[selected], 0); - Dbprintf("Recorded %x %x%08x", selected, high[selected], low[selected]); + CmdHIDdemodFSK(1, &tops[selected], &high[selected], &low[selected], 0); + if (tops[selected] > 0) + Dbprintf("Recorded %x %x%08x%08x", selected, tops[selected], high[selected], low[selected]); + else + Dbprintf("Recorded %x %x%08x", selected, high[selected], low[selected]); LEDsoff(); LED(selected + 1, 0); @@ -705,7 +732,10 @@ void SamyRun() LED(LED_ORANGE, 0); // record - Dbprintf("Cloning %x %x%08x", selected, high[selected], low[selected]); + if (tops[selected] > 0) + Dbprintf("Cloning %x %x%08x%08x", selected, tops[selected], high[selected], low[selected]); + else + Dbprintf("Cloning %x %x%08x", selected, high[selected], low[selected]); // wait for button to be released while(BUTTON_PRESS()) @@ -714,8 +744,11 @@ void SamyRun() /* need this delay to prevent catching some weird data */ SpinDelay(500); - CopyHIDtoT55x7(0, high[selected], low[selected], 0); - Dbprintf("Cloned %x %x%08x", selected, high[selected], low[selected]); + CopyHIDtoT55x7(tops[selected] & 0x000FFFFF, high[selected], low[selected], (tops[selected] != 0 && ((high[selected]& 0xFFFFFFC0) != 0))); + if (tops[selected] > 0) + Dbprintf("Cloned %x %x%08x%08x", selected, tops[selected], high[selected], low[selected]); + else + Dbprintf("Cloned %x %x%08x", selected, high[selected], low[selected]); LEDsoff(); LED(selected + 1, 0); @@ -748,8 +781,12 @@ void SamyRun() // wait for button to be released while(BUTTON_PRESS()) WDT_HIT(); - Dbprintf("%x %x%08x", selected, high[selected], low[selected]); - CmdHIDsimTAG(high[selected], low[selected], 0); + if (tops[selected] > 0) + Dbprintf("%x %x%08x%08x", selected, tops[selected], high[selected], low[selected]); + else + Dbprintf("%x %x%08x", selected, high[selected], low[selected]); + + CmdHIDsimTAG(tops[selected], high[selected], low[selected], 0); DbpString("Done playing"); if (BUTTON_HELD(1000) > 0) { @@ -814,13 +851,15 @@ static const int LIGHT_LEN = sizeof(LIGHT_SCHEME)/sizeof(LIGHT_SCHEME[0]); void ListenReaderField(int limit) { - int lf_av, lf_av_new, lf_baseline= 0, lf_max; - int hf_av, hf_av_new, hf_baseline= 0, hf_max; + int lf_av, lf_av_new=0, lf_baseline= 0, lf_max; + int hf_av, hf_av_new=0, hf_baseline= 0, hf_max; int mode=1, display_val, display_max, i; -#define LF_ONLY 1 -#define HF_ONLY 2 -#define REPORT_CHANGE 10 // report new values only if they have changed at least by REPORT_CHANGE +#define LF_ONLY 1 +#define HF_ONLY 2 +#define REPORT_CHANGE_PERCENT 5 // report new values only if they have changed at least by REPORT_CHANGE_PERCENT +#define MIN_HF_FIELD 300 // in mode 1 signal HF field greater than MIN_HF_FIELD above baseline +#define MIN_LF_FIELD 1200 // in mode 1 signal LF field greater than MIN_LF_FIELD above baseline // switch off FPGA - we don't want to measure our own signal @@ -829,23 +868,23 @@ void ListenReaderField(int limit) LEDsoff(); - lf_av = lf_max = AvgAdc(ADC_CHAN_LF); + lf_av = lf_max = AvgAdc_Voltage_LF(); if(limit != HF_ONLY) { - Dbprintf("LF 125/134kHz Baseline: %dmV", (MAX_ADC_LF_VOLTAGE * lf_av) >> 10); + Dbprintf("LF 125/134kHz Baseline: %dmV", lf_av); lf_baseline = lf_av; } - hf_av = hf_max = AvgAdc(ADC_CHAN_HF); - + hf_av = hf_max = AvgAdc_Voltage_HF(); + if (limit != LF_ONLY) { - Dbprintf("HF 13.56MHz Baseline: %dmV", (MAX_ADC_HF_VOLTAGE * hf_av) >> 10); + Dbprintf("HF 13.56MHz Baseline: %dmV", hf_av); hf_baseline = hf_av; } for(;;) { + SpinDelay(500); if (BUTTON_PRESS()) { - SpinDelay(500); switch (mode) { case 1: mode=2; @@ -858,21 +897,22 @@ void ListenReaderField(int limit) return; break; } + while (BUTTON_PRESS()); } WDT_HIT(); if (limit != HF_ONLY) { if(mode == 1) { - if (ABS(lf_av - lf_baseline) > REPORT_CHANGE) + if (lf_av - lf_baseline > MIN_LF_FIELD) LED_D_ON(); else LED_D_OFF(); } - lf_av_new = AvgAdc(ADC_CHAN_LF); + lf_av_new = AvgAdc_Voltage_LF(); // see if there's a significant change - if(ABS(lf_av - lf_av_new) > REPORT_CHANGE) { - Dbprintf("LF 125/134kHz Field Change: %5dmV", (MAX_ADC_LF_VOLTAGE * lf_av_new) >> 10); + if (ABS((lf_av - lf_av_new)*100/(lf_av?lf_av:1)) > REPORT_CHANGE_PERCENT) { + Dbprintf("LF 125/134kHz Field Change: %5dmV", lf_av_new); lf_av = lf_av_new; if (lf_av > lf_max) lf_max = lf_av; @@ -881,16 +921,17 @@ void ListenReaderField(int limit) if (limit != LF_ONLY) { if (mode == 1){ - if (ABS(hf_av - hf_baseline) > REPORT_CHANGE) + if (hf_av - hf_baseline > MIN_HF_FIELD) LED_B_ON(); else LED_B_OFF(); } - hf_av_new = AvgAdc(ADC_CHAN_HF); + hf_av_new = AvgAdc_Voltage_HF(); + // see if there's a significant change - if(ABS(hf_av - hf_av_new) > REPORT_CHANGE) { - Dbprintf("HF 13.56MHz Field Change: %5dmV", (MAX_ADC_HF_VOLTAGE * hf_av_new) >> 10); + if (ABS((hf_av - hf_av_new)*100/(hf_av?hf_av:1)) > REPORT_CHANGE_PERCENT) { + Dbprintf("HF 13.56MHz Field Change: %5dmV", hf_av_new); hf_av = hf_av_new; if (hf_av > hf_max) hf_max = hf_av; @@ -947,10 +988,10 @@ void UsbPacketReceived(uint8_t *packet, int len) cmd_send(CMD_ACK,SnoopLF(),0,0,0,0); break; case CMD_HID_DEMOD_FSK: - CmdHIDdemodFSK(c->arg[0], 0, 0, 1); + CmdHIDdemodFSK(c->arg[0], 0, 0, 0, 1); break; case CMD_HID_SIM_TAG: - CmdHIDsimTAG(c->arg[0], c->arg[1], 1); + CmdHIDsimTAG(c->arg[0], c->arg[1], c->arg[2], 1); break; case CMD_FSK_SIM_TAG: CmdFSKsimTAG(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); @@ -1090,7 +1131,7 @@ void UsbPacketReceived(uint8_t *packet, int len) #ifdef WITH_LEGICRF case CMD_SIMULATE_TAG_LEGIC_RF: - LegicRfSimulate(c->arg[0], c->arg[1], c->arg[2]); + LegicRfSimulate(c->arg[0]); break; case CMD_WRITER_LEGIC_RF: @@ -1199,6 +1240,9 @@ void UsbPacketReceived(uint8_t *packet, int len) break; // Work with "magic Chinese" card + case CMD_MIFARE_CWIPE: + MifareCWipe(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; case CMD_MIFARE_CSETBLOCK: MifareCSetBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; @@ -1257,6 +1301,31 @@ void UsbPacketReceived(uint8_t *packet, int len) HfSnoop(c->arg[0], c->arg[1]); break; #endif +#ifdef WITH_SMARTCARD + case CMD_SMART_ATR: { + SmartCardAtr(); + break; + } + case CMD_SMART_SETCLOCK:{ + SmartCardSetClock(c->arg[0]); + break; + } + case CMD_SMART_RAW: { + SmartCardRaw(c->arg[0], c->arg[1], c->d.asBytes); + break; + } + case CMD_SMART_UPLOAD: { + // upload file from client + uint8_t *mem = BigBuf_get_addr(); + memcpy( mem + c->arg[0], c->d.asBytes, USB_CMD_DATA_SIZE); + cmd_send(CMD_ACK,1,0,0,0,0); + break; + } + case CMD_SMART_UPGRADE: { + SmartCardUpgrade(c->arg[0]); + break; + } +#endif case CMD_BUFF_CLEAR: BigBuf_Clear(); @@ -1394,7 +1463,7 @@ void __attribute__((noreturn)) AppMain(void) LED_A_OFF(); // Init USB device - usb_enable(); + usb_enable(); // The FPGA gets its clock from us from PCK0 output, so set that up. AT91C_BASE_PIOA->PIO_BSR = GPIO_PCK0; @@ -1432,7 +1501,7 @@ void __attribute__((noreturn)) AppMain(void) } WDT_HIT(); -#ifdef WITH_LF +#ifdef WITH_LF_StandAlone #ifndef WITH_ISO14443a_StandAlone if (BUTTON_HELD(1000) > 0) SamyRun(); diff --git a/armsrc/apps.h b/armsrc/apps.h index aa5b47fb..6f728c61 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -42,9 +42,10 @@ void Dbprintf(const char *fmt, ...); void Dbhexdump(int len, uint8_t *d, bool bAsci); // ADC Vref = 3300mV, and an (10M+1M):1M voltage divider on the HF input can measure voltages up to 36300 mV -#define MAX_ADC_HF_VOLTAGE 36300 +#define MAX_ADC_HF_VOLTAGE_LOW 36300 // ADC Vref = 3300mV, and an (10000k+240k):240k voltage divider on the LF input can measure voltages up to 140800 mV -#define MAX_ADC_LF_VOLTAGE 140800 +#define MAX_ADC_HF_VOLTAGE_HIGH 140800 +#define MAX_ADC_LF_VOLTAGE 140800 int AvgAdc(int ch); void ToSendStuffBit(int b); @@ -68,11 +69,11 @@ void AcquireTiType(void); void AcquireRawBitsTI(void); void SimulateTagLowFrequency(int period, int gap, int ledcontrol); void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen); -void CmdHIDsimTAG(int hi, int lo, int ledcontrol); +void CmdHIDsimTAG(int hi2, int hi, int lo, int ledcontrol); void CmdFSKsimTAG(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream); void CmdASKsimTag(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream); void CmdPSKsimTag(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream); -void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol); +void CmdHIDdemodFSK(int findone, int *high2, int *high, int *low, int ledcontrol); void CmdAWIDdemodFSK(int findone, int *high, int *low, int ledcontrol); // Realtime demodulation mode for AWID26 void CmdEM410xdemod(int findone, int *high, int *low, int ledcontrol); void CmdIOdemodFSK(int findone, int *high, int *low, int ledcontrol); @@ -99,14 +100,9 @@ void ReadSTMemoryIso14443b(uint32_t); void RAMFUNC SnoopIso14443b(void); void SendRawCommand14443B(uint32_t, uint32_t, uint8_t, uint8_t[]); -/// iso14443a.h -void RAMFUNC SnoopIso14443a(uint8_t param); -void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data); -void ReaderIso14443a(UsbCommand * c); // Also used in iclass.c bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t len, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *parity); -void iso14a_set_trigger(bool enable); void RAMFUNC SniffMifare(uint8_t param); @@ -115,8 +111,6 @@ void EPA_PACE_Collect_Nonce(UsbCommand * c); void EPA_PACE_Replay(UsbCommand *c); // mifarecmd.h -void ReaderMifare(bool first_try); -int32_t dist_nt(uint32_t nt1, uint32_t nt2); void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *data); void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareUC_Auth(uint8_t arg0, uint8_t *datain); @@ -127,14 +121,15 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain); -void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); +void MifareChkKeys(uint16_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); void MifareSetDbgLvl(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareEMemSet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareEMemGet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); -void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); // Work with "magic Chinese" card +void MifareCWipe(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); // Work with "magic Chinese" card +void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareCIdent(); // is "magic chinese" card? void MifareUSetPwd(uint8_t arg0, uint8_t *datain); diff --git a/armsrc/epa.c b/armsrc/epa.c index f434aa34..fd71430b 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -530,7 +530,7 @@ int EPA_Setup() // power up the field iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); // select the card - return_code = iso14443a_select_card(uid, &card_select_info, NULL, true, 0); + return_code = iso14443a_select_card(uid, &card_select_info, NULL, true, 0, false); if (return_code == 1) { // send the PPS request ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL); diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 1deb5b7d..45294d60 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -10,20 +10,21 @@ // mode once it is configured. //----------------------------------------------------------------------------- +#include "fpgaloader.h" + #include #include #include -#include "fpgaloader.h" +#include "apps.h" +#include "fpga.h" #include "proxmark3.h" #include "util.h" #include "string.h" #include "BigBuf.h" #include "zlib.h" -extern void Dbprintf(const char *fmt, ...); - // remember which version of the bitstream we have already downloaded to the FPGA -static int downloaded_bitstream = FPGA_BITSTREAM_ERR; +static int downloaded_bitstream = 0; // this is where the bitstreams are located in memory: extern uint8_t _binary_obj_fpga_all_bit_z_start, _binary_obj_fpga_all_bit_z_end; @@ -31,10 +32,7 @@ extern uint8_t _binary_obj_fpga_all_bit_z_start, _binary_obj_fpga_all_bit_z_end; static uint8_t *fpga_image_ptr = NULL; static uint32_t uncompressed_bytes_cnt; -static const uint8_t _bitparse_fixed_header[] = {0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01}; -#define FPGA_BITSTREAM_FIXED_HEADER_SIZE sizeof(_bitparse_fixed_header) #define OUTPUT_BUFFER_LEN 80 -#define FPGA_INTERLEAVE_SIZE 288 //----------------------------------------------------------------------------- // Set up the Serial Peripheral Interface as master @@ -114,10 +112,10 @@ void SetupSpi(int mode) } //----------------------------------------------------------------------------- -// Set up the synchronous serial port, with the one set of options that we -// always use when we are talking to the FPGA. Both RX and TX are enabled. +// Set up the synchronous serial port with the set of options that fits +// the FPGA mode. Both RX and TX are always enabled. //----------------------------------------------------------------------------- -void FpgaSetupSsc(void) +void FpgaSetupSsc(uint8_t FPGA_mode) { // First configure the GPIOs, and get ourselves a clock. AT91C_BASE_PIOA->PIO_ASR = @@ -136,11 +134,15 @@ void FpgaSetupSsc(void) // on RX clock rising edge, sampled on falling edge AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); - // 8 bits per transfer, no loopback, MSB first, 1 transfer per sync + // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync // pulse, no output sync - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + if ((FPGA_mode & 0xe0) == FPGA_MAJOR_MODE_HF_READER_RX_XCORR) { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } else { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } - // clock comes from TK pin, no clock output, outputs change on falling + // TX clock comes from TK pin, no clock output, outputs change on falling // edge of TK, sample on rising edge of TK, start on positive-going edge of sync AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); @@ -156,17 +158,16 @@ void FpgaSetupSsc(void) // ourselves, not to another buffer). The stuff to manipulate those buffers // is in apps.h, because it should be inlined, for speed. //----------------------------------------------------------------------------- -bool FpgaSetupSscDma(uint8_t *buf, int len) +bool FpgaSetupSscDma(uint8_t *buf, uint16_t sample_count) { if (buf == NULL) return false; - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // transfer to this memory address - AT91C_BASE_PDC_SSC->PDC_RCR = len; // transfer this many bytes - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address - AT91C_BASE_PDC_SSC->PDC_RNCR = len; // ... with same number of bytes - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // go! - + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // transfer to this memory address + AT91C_BASE_PDC_SSC->PDC_RCR = sample_count; // transfer this many samples + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address + AT91C_BASE_PDC_SSC->PDC_RNCR = sample_count; // ... with same number of samples AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // go! + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // go! return true; } @@ -201,7 +202,7 @@ static int get_from_fpga_combined_stream(z_streamp compressed_fpga_stream, uint8 //---------------------------------------------------------------------------- static int get_from_fpga_stream(int bitstream_version, z_streamp compressed_fpga_stream, uint8_t *output_buffer) { - while((uncompressed_bytes_cnt / FPGA_INTERLEAVE_SIZE) % FPGA_BITSTREAM_MAX != (bitstream_version - 1)) { + while((uncompressed_bytes_cnt / FPGA_INTERLEAVE_SIZE) % fpga_bitstream_num != (bitstream_version - 1)) { // skip undesired data belonging to other bitstream_versions get_from_fpga_combined_stream(compressed_fpga_stream, output_buffer); } @@ -234,7 +235,7 @@ static bool reset_fpga_stream(int bitstream_version, z_streamp compressed_fpga_s // initialize z_stream structure for inflate: compressed_fpga_stream->next_in = &_binary_obj_fpga_all_bit_z_start; - compressed_fpga_stream->avail_in = &_binary_obj_fpga_all_bit_z_start - &_binary_obj_fpga_all_bit_z_end; + compressed_fpga_stream->avail_in = &_binary_obj_fpga_all_bit_z_end - &_binary_obj_fpga_all_bit_z_start; compressed_fpga_stream->next_out = output_buffer; compressed_fpga_stream->avail_out = OUTPUT_BUFFER_LEN; compressed_fpga_stream->zalloc = &fpga_inflate_malloc; @@ -248,8 +249,8 @@ static bool reset_fpga_stream(int bitstream_version, z_streamp compressed_fpga_s header[i] = get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); } - // Check for a valid .bit file (starts with _bitparse_fixed_header) - if(memcmp(_bitparse_fixed_header, header, FPGA_BITSTREAM_FIXED_HEADER_SIZE) == 0) { + // Check for a valid .bit file (starts with bitparse_fixed_header) + if(memcmp(bitparse_fixed_header, header, FPGA_BITSTREAM_FIXED_HEADER_SIZE) == 0) { return true; } else { return false; @@ -427,89 +428,21 @@ void FpgaDownloadAndGo(int bitstream_version) } unsigned int bitstream_length; - if(bitparse_find_section(bitstream_version, 'e', &bitstream_length, &compressed_fpga_stream, output_buffer)) { + if (bitparse_find_section(bitstream_version, 'e', &bitstream_length, &compressed_fpga_stream, output_buffer)) { DownloadFPGA(bitstream_version, bitstream_length, &compressed_fpga_stream, output_buffer); downloaded_bitstream = bitstream_version; } inflateEnd(&compressed_fpga_stream); + // turn off antenna + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // free eventually allocated BigBuf memory BigBuf_free(); BigBuf_Clear_ext(false); } -//----------------------------------------------------------------------------- -// Gather version information from FPGA image. Needs to decompress the begin -// of the respective (HF or LF) image. -// Note: decompression makes use of (i.e. overwrites) BigBuf[]. It is therefore -// advisable to call this only once and store the results for later use. -//----------------------------------------------------------------------------- -void FpgaGatherVersion(int bitstream_version, char *dst, int len) -{ - unsigned int fpga_info_len; - char tempstr[40] = {0x00}; - z_stream compressed_fpga_stream; - uint8_t output_buffer[OUTPUT_BUFFER_LEN] = {0x00}; - - dst[0] = '\0'; - - // ensure that we can allocate enough memory for decompression: - BigBuf_free(); BigBuf_Clear_ext(false); - - if (!reset_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer)) - return; - - if(bitparse_find_section(bitstream_version, 'a', &fpga_info_len, &compressed_fpga_stream, output_buffer)) { - for (uint16_t i = 0; i < fpga_info_len; i++) { - char c = (char)get_from_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer); - if (i < sizeof(tempstr)) { - tempstr[i] = c; - } - } - if (!memcmp("fpga_lf", tempstr, 7)) - strncat(dst, "LF ", len-1); - else if (!memcmp("fpga_hf", tempstr, 7)) - strncat(dst, "HF ", len-1); - } - strncat(dst, "FPGA image built", len-1); - if(bitparse_find_section(bitstream_version, 'b', &fpga_info_len, &compressed_fpga_stream, output_buffer)) { - strncat(dst, " for ", len-1); - for (uint16_t i = 0; i < fpga_info_len; i++) { - char c = (char)get_from_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer); - if (i < sizeof(tempstr)) { - tempstr[i] = c; - } - } - strncat(dst, tempstr, len-1); - } - if(bitparse_find_section(bitstream_version, 'c', &fpga_info_len, &compressed_fpga_stream, output_buffer)) { - strncat(dst, " on ", len-1); - for (uint16_t i = 0; i < fpga_info_len; i++) { - char c = (char)get_from_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer); - if (i < sizeof(tempstr)) { - tempstr[i] = c; - } - } - strncat(dst, tempstr, len-1); - } - if(bitparse_find_section(bitstream_version, 'd', &fpga_info_len, &compressed_fpga_stream, output_buffer)) { - strncat(dst, " at ", len-1); - for (uint16_t i = 0; i < fpga_info_len; i++) { - char c = (char)get_from_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer); - if (i < sizeof(tempstr)) { - tempstr[i] = c; - } - } - strncat(dst, tempstr, len-1); - } - - strncat(dst, "\n", len-1); - - inflateEnd(&compressed_fpga_stream); -} - - //----------------------------------------------------------------------------- // Send a 16 bit command/data pair to the FPGA. // The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 @@ -559,12 +492,8 @@ void SetAdcMuxFor(uint32_t whichGpio) } void Fpga_print_status(void) { - Dbprintf("Fgpa"); - switch(downloaded_bitstream) { - case FPGA_BITSTREAM_HF: Dbprintf(" mode....................HF"); break; - case FPGA_BITSTREAM_LF: Dbprintf(" mode....................LF"); break; - default: Dbprintf(" mode....................%d", downloaded_bitstream); break; - } + Dbprintf("Currently loaded FPGA image:"); + Dbprintf(" %s", fpga_version_information[downloaded_bitstream-1]); } int FpgaGetCurrent() { diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 7dfc5c12..5aff6505 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -10,13 +10,18 @@ // mode once it is configured. //----------------------------------------------------------------------------- +#ifndef __FPGALOADER_H +#define __FPGALOADER_H + +#include +#include + void FpgaSendCommand(uint16_t cmd, uint16_t v); void FpgaWriteConfWord(uint8_t v); void FpgaDownloadAndGo(int bitstream_version); -void FpgaGatherVersion(int bitstream_version, char *dst, int len); -void FpgaSetupSsc(void); +void FpgaSetupSsc(uint8_t mode); void SetupSpi(int mode); -bool FpgaSetupSscDma(uint8_t *buf, int len); +bool FpgaSetupSscDma(uint8_t *buf, uint16_t sample_count); void Fpga_print_status(); int FpgaGetCurrent(); #define FpgaDisableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; @@ -24,12 +29,9 @@ int FpgaGetCurrent(); void SetAdcMuxFor(uint32_t whichGpio); // definitions for multiple FPGA config files support -#define FPGA_BITSTREAM_MAX 2 // the total number of FPGA bitstreams (configs) -#define FPGA_BITSTREAM_ERR 0 #define FPGA_BITSTREAM_LF 1 #define FPGA_BITSTREAM_HF 2 - // Definitions for the FPGA commands. #define FPGA_CMD_SET_CONFREG (1<<12) #define FPGA_CMD_SET_DIVISOR (2<<12) @@ -72,3 +74,5 @@ void SetAdcMuxFor(uint32_t whichGpio); #define FPGA_HF_ISO14443A_TAGSIM_MOD (2<<0) #define FPGA_HF_ISO14443A_READER_LISTEN (3<<0) #define FPGA_HF_ISO14443A_READER_MOD (4<<0) + +#endif diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index d06af443..e492c474 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -37,7 +37,7 @@ void HfSnoop(int samplesToSkip, int triggersToSkip) // Select correct configs FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SNOOP); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SNOOP); diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 7702025b..8e690a7b 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -813,13 +813,13 @@ void SnoopHitag(uint32_t type) { int lastbit; bool bSkip; int tag_sof; - byte_t rx[HITAG_FRAME_LEN]; + byte_t rx[HITAG_FRAME_LEN] = {0}; size_t rxlen=0; FpgaDownloadAndGo(FPGA_BITSTREAM_LF); // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); auth_table_len = 0; @@ -1032,7 +1032,7 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); auth_table_len = 0; @@ -1217,7 +1217,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { int reset_sof; int tag_sof; int t_wait = HITAG_T_WAIT_MAX; - bool bStop; + bool bStop = false; bool bQuitTraceFull = false; FpgaDownloadAndGo(FPGA_BITSTREAM_LF); @@ -1225,7 +1225,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { bSuccessful = false; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); //DbpString("Starting Hitag reader family"); @@ -1324,7 +1324,6 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { frame_count = 0; response = 0; lastbit = 1; - bStop = false; // Tag specific configuration settings (sof, timings, etc.) if (htf < 10){ @@ -1369,46 +1368,46 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { // By default reset the transmission buffer tx = txbuf; switch(htf) { - case RHT2F_PASSWORD: { - bStop = !hitag2_password(rx,rxlen,tx,&txlen); - } break; - case RHT2F_AUTHENTICATE: { - bStop = !hitag2_authenticate(rx,rxlen,tx,&txlen); - } break; - case RHT2F_CRYPTO: { - bStop = !hitag2_crypto(rx,rxlen,tx,&txlen, false); - } break; - case RHT2F_TEST_AUTH_ATTEMPTS: { - bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen); - } break; - case RHT2F_UID_ONLY: { - bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen); - attempt_count++; //attempt 3 times to get uid then quit - if (!bStop && attempt_count == 3) bStop = true; - } break; - default: { - Dbprintf("Error, unknown function: %d",htf); - return; - } break; + case RHT2F_PASSWORD: { + bStop = !hitag2_password(rx,rxlen,tx,&txlen); + } break; + case RHT2F_AUTHENTICATE: { + bStop = !hitag2_authenticate(rx,rxlen,tx,&txlen); + } break; + case RHT2F_CRYPTO: { + bStop = !hitag2_crypto(rx,rxlen,tx,&txlen, false); + } break; + case RHT2F_TEST_AUTH_ATTEMPTS: { + bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen); + } break; + case RHT2F_UID_ONLY: { + bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen); + attempt_count++; //attempt 3 times to get uid then quit + if (!bStop && attempt_count == 3) bStop = true; + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; } - + // 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; - + // Wait for HITAG_T_WAIT_2 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 occured halfway the period. with respect to this falling edge, // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. // All timer values are in terms of T0 units while(AT91C_BASE_TC0->TC_CV < T0*(t_wait+(HITAG_T_TAG_HALF_PERIOD*lastbit))); - + //Dbprintf("DEBUG: Sending reader frame"); // Transmit the reader frame hitag_reader_send_frame(tx,txlen); - // Enable and reset external trigger in timer for capturing future frames + // Enable and reset external trigger in timer for capturing future frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // Add transmitted frame to total count @@ -1521,7 +1520,11 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); //Dbprintf("frame received: %d",frame_count); //DbpString("All done"); - cmd_send(CMD_ACK,bSuccessful,0,0,(byte_t*)tag.sectors,48); + if (bSuccessful) + cmd_send(CMD_ACK,bSuccessful,0,0,(byte_t*)tag.sectors,48); + else + cmd_send(CMD_ACK,bSuccessful,0,0,0,0); + } void WriterHitag(hitag_function htf, hitag_data* htd, int page) { @@ -1545,7 +1548,7 @@ void WriterHitag(hitag_function htf, hitag_data* htd, int page) { bSuccessful = false; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); //DbpString("Starting Hitag reader family"); diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index dc2281b9..f6ba0c6b 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -210,7 +210,7 @@ static void hitag_send_bit(int bit) { ; LOW(GPIO_SSC_DOUT); while (AT91C_BASE_TC0->TC_CV < T0 * 32) - ;; + ; } LED_A_OFF(); break; @@ -945,7 +945,6 @@ void SimulateHitagSTag(bool tag_mem_supplied, byte_t* data) { int i, j; byte_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; -//bool bQuitTraceFull = false; bQuiet = false; byte_t txbuf[HITAG_FRAME_LEN]; byte_t* tx = txbuf; @@ -953,7 +952,7 @@ void SimulateHitagSTag(bool tag_mem_supplied, byte_t* data) { BigBuf_free(); // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); DbpString("Starting HitagS simulation"); @@ -985,39 +984,39 @@ void SimulateHitagSTag(bool tag_mem_supplied, byte_t* data) { tag.max_page=0; //con1 tag.auth=0; - if((tag.pages[1][2]&0x80)==1) + if (tag.pages[1][2]&0x80) tag.auth=1; tag.LCON=0; - if((tag.pages[1][2]&0x2)==1) + if (tag.pages[1][2]&0x2) tag.LCON=1; tag.LKP=0; - if((tag.pages[1][2]&0x1)==1) + if (tag.pages[1][2]&0x1) tag.LKP=1; //con2 //0=read write 1=read only tag.LCK7=0; - if((tag.pages[1][1]&0x80)==1) + if (tag.pages[1][1]&0x80) tag.LCK7=1; tag.LCK6=0; - if((tag.pages[1][1]&0x40)==1) + if (tag.pages[1][1]&0x40) tag.LCK6=1; tag.LCK5=0; - if((tag.pages[1][1]&0x20)==1) + if (tag.pages[1][1]&0x20) tag.LCK5=1; tag.LCK4=0; - if((tag.pages[1][1]&0x10)==1) + if (tag.pages[1][1]&0x10) tag.LCK4=1; tag.LCK3=0; - if((tag.pages[1][1]&0x8)==1) + if (tag.pages[1][1]&0x8) tag.LCK3=1; tag.LCK2=0; - if((tag.pages[1][1]&0x4)==1) + if (tag.pages[1][1]&0x4) tag.LCK2=1; tag.LCK1=0; - if((tag.pages[1][1]&0x2)==1) + if (tag.pages[1][1]&0x2) tag.LCK1=1; tag.LCK0=0; - if((tag.pages[1][1]&0x1)==1) + if (tag.pages[1][1]&0x1) tag.LCK0=1; // Set up simulator mode, frequency divisor which will drive the FPGA @@ -1216,7 +1215,7 @@ void ReadHitagS(hitag_function htf, hitag_data* htd) { bSuccessful = false; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); bQuiet = false; @@ -1560,7 +1559,7 @@ void WritePageHitagS(hitag_function htf, hitag_data* htd,int page_) { tag.tstate = NO_OP; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); bQuiet = false; @@ -1847,7 +1846,7 @@ void check_challenges(bool file_given, byte_t* data) { bSuccessful = false; // Clean up trace and prepare it for storing frames - set_tracing(TRUE); + set_tracing(true); clear_trace(); bQuiet = false; diff --git a/armsrc/i2c.c b/armsrc/i2c.c new file mode 100644 index 00000000..7efb1fd3 --- /dev/null +++ b/armsrc/i2c.c @@ -0,0 +1,823 @@ +//----------------------------------------------------------------------------- +// Willok, June 2018 +// Edits by Iceman, July 2018 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// The main i2c code, for communications with smart card module +//----------------------------------------------------------------------------- + +#include "i2c.h" + +#include +#include +#include "string.h" //for memset memcmp +#include "proxmark3.h" +#include "mifareutil.h" // for MF_DBGLEVEL +#include "BigBuf.h" +#include "apps.h" + +#ifdef WITH_SMARTCARD +#include "smartcard.h" +#endif + + +// ¶¨ÒåÁ¬½ÓÒý½Å +#define GPIO_RST AT91C_PIO_PA1 +#define GPIO_SCL AT91C_PIO_PA5 +#define GPIO_SDA AT91C_PIO_PA7 + +#define SCL_H HIGH(GPIO_SCL) +#define SCL_L LOW(GPIO_SCL) +#define SDA_H HIGH(GPIO_SDA) +#define SDA_L LOW(GPIO_SDA) + +#define SCL_read (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SCL) +#define SDA_read (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SDA) + +#define I2C_ERROR "I2C_WaitAck Error" + +static volatile unsigned long c; + +// Ö±½ÓʹÓÃÑ­»·À´ÑÓʱ£¬Ò»¸öÑ­»· 6 ÌõÖ¸Á48M£¬ Delay=1 ´ó¸ÅΪ 200kbps +// timer. +// I2CSpinDelayClk(4) = 12.31us +// I2CSpinDelayClk(1) = 3.07us +static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { + for (c = delay * 2; c; c--) {}; +} + +// ͨѶÑÓ³Ùº¯Êý communication delay function +#define I2C_DELAY_1CLK I2CSpinDelayClk(1) +#define I2C_DELAY_2CLK I2CSpinDelayClk(2) +#define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x)) + + +#define ISO7618_MAX_FRAME 255 + +static void I2C_init(void) { + // Configure reset pin + AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; // disable pull up resistor + AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST; // push-pull output (multidriver disabled) + + // Configure SCL and SDA pins + AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_SCL | GPIO_SDA); // enable pull up resistor + AT91C_BASE_PIOA->PIO_MDER |= (GPIO_SCL | GPIO_SDA); // open drain output (multidriver enabled) - requires external pull up resistor + + // set all three outputs to high + AT91C_BASE_PIOA->PIO_SODR |= (GPIO_SCL | GPIO_SDA | GPIO_RST); + + // configure all three pins as output, controlled by PIOA + AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); + AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); +} + + +// ÉèÖø´Î»×´Ì¬ +// set the reset state +static void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) { + if (LineRST) + HIGH(GPIO_RST); + else + LOW(GPIO_RST); + + if (LineSCK) + HIGH(GPIO_SCL); + else + LOW(GPIO_SCL); + + if (LineSDA) + HIGH(GPIO_SDA); + else + LOW(GPIO_SDA); +} + +// ¸´Î»½øÈëÖ÷³ÌÐò +// Reset the SIM_Adapter, then enter the main program +// Note: the SIM_Adapter will not enter the main program after power up. Please run this function before use SIM_Adapter. +static void I2C_Reset_EnterMainProgram(void) { + I2C_SetResetStatus(0, 0, 0); // À­µÍ¸´Î»Ïß + SpinDelay(30); + I2C_SetResetStatus(1, 0, 0); // ½â³ý¸´Î» + SpinDelay(30); + I2C_SetResetStatus(1, 1, 1); // À­¸ßÊý¾ÝÏß + SpinDelay(10); +} + +// µÈ´ýʱÖÓ±ä¸ß +// Wait for the clock to go High. +static bool WaitSCL_H_delay(uint32_t delay) { + while (delay--) { + if (SCL_read) { + return true; + } + I2C_DELAY_1CLK; + } + return false; +} + +// 15000 * 3.07us = 46050us. 46.05ms +static bool WaitSCL_H(void) { + return WaitSCL_H_delay(15000); +} + +bool WaitSCL_L_delay(uint32_t delay) { + while (delay--) { + if (!SCL_read) { + return true; + } + I2C_DELAY_1CLK; + } + return false; +} + +bool WaitSCL_L(void) { + return WaitSCL_L_delay(15000); +} + +static bool I2C_Start(void) { + + I2C_DELAY_XCLK(4); + SDA_H; I2C_DELAY_1CLK; + SCL_H; + if (!WaitSCL_H()) return false; + + I2C_DELAY_2CLK; + + if (!SCL_read) return false; + if (!SDA_read) return false; + + SDA_L; I2C_DELAY_2CLK; + return true; +} + +// send i2c STOP +static void I2C_Stop(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_L; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + if (!WaitSCL_H()) return; + SDA_H; + I2C_DELAY_XCLK(8); +} + +static bool I2C_WaitAck(void) { + SCL_L; I2C_DELAY_1CLK; + SDA_H; I2C_DELAY_1CLK; + SCL_H; + if (!WaitSCL_H()) + return false; + + I2C_DELAY_2CLK; + I2C_DELAY_2CLK; + if (SDA_read) { + SCL_L; + return false; + } + SCL_L; + return true; +} + +static void I2C_SendByte(uint8_t data) { + uint8_t bits = 8; + + while (bits--) { + SCL_L; I2C_DELAY_1CLK; + + if (data & 0x80) + SDA_H; + else + SDA_L; + + data <<= 1; + + I2C_DELAY_1CLK; + + SCL_H; + if (!WaitSCL_H()) + return; + + I2C_DELAY_2CLK; + } + SCL_L; +} + +bool I2C_is_available(void) { + I2C_init(); + I2C_Reset_EnterMainProgram(); + if (!I2C_Start()) // some other device is active on the bus + return true; + I2C_SendByte(I2C_DEVICE_ADDRESS_MAIN & 0xFE); + if (!I2C_WaitAck()) { // no response from smartcard reader + I2C_Stop(); + return false; + } + I2C_Stop(); + return true; +} + +#ifdef WITH_SMARTCARD +// ¸´Î»½øÈëÒýµ¼Ä£Ê½ +// Reset the SIM_Adapter, then enter the bootloader program +// Reserve£ºFor firmware update. +static void I2C_Reset_EnterBootloader(void) { + I2C_SetResetStatus(0, 1, 1); // À­µÍ¸´Î»Ïß + SpinDelay(100); + I2C_SetResetStatus(1, 1, 1); // ½â³ý¸´Î» + SpinDelay(10); +} + +// Wait max 300ms or until SCL goes LOW. +// Which ever comes first +static bool WaitSCL_L_300ms(void) { + volatile uint16_t delay = 310; + while ( delay-- ) { + // exit on SCL LOW + if (!SCL_read) + return true; + + SpinDelay(1); + } + return (delay == 0); +} + +static bool I2C_WaitForSim() { + // variable delay here. + if (!WaitSCL_L_300ms()) + return false; + + // 8051 speaks with smart card. + // 1000*50*3.07 = 153.5ms + // 1byte transfer == 1ms with max frame being 256bytes + if (!WaitSCL_H_delay(10 * 1000 * 50)) + return false; + + return true; +} + +// Send i2c ACK +static void I2C_Ack(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_L; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + if (!WaitSCL_H()) return; + SCL_L; I2C_DELAY_2CLK; +} + +// Send i2c NACK +static void I2C_NoAck(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_H; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + if (!WaitSCL_H()) return; + SCL_L; I2C_DELAY_2CLK; +} + +static int16_t I2C_ReadByte(void) { + uint8_t bits = 8, b = 0; + + SDA_H; + while (bits--) { + b <<= 1; + SCL_L; + if (!WaitSCL_L()) return -2; + + I2C_DELAY_1CLK; + + SCL_H; + if (!WaitSCL_H()) return -1; + + I2C_DELAY_1CLK; + if (SDA_read) + b |= 0x01; + } + SCL_L; + return b; +} + +// Sends one byte ( command to be written, SlaveDevice address) +static bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { + bool bBreak = true; + do { + if (!I2C_Start()) + return false; + //[C0] + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +// дÈë1×Ö½ÚÊý¾Ý £¨´ýдÈëÊý¾Ý£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© +// Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ). +static bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { + bool bBreak = true; + do { + if (!I2C_Start()) + return false; + + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(data); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +// дÈë1´®Êý¾Ý£¨´ýдÈëÊý×鵨ַ£¬´ýдÈ볤¶È£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© +//Sends a string of data (Array, length, command to be written , SlaveDevice address ). +// len = uint8 (max buffer to write 256bytes) +static bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { + bool bBreak = true; + do { + if (!I2C_Start()) + return false; + + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + while (len) { + + I2C_SendByte(*data); + if (!I2C_WaitAck()) + break; + + len--; + data++; + } + + if (len == 0) + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +// ¶Á³ö1´®Êý¾Ý£¨´æ·Å¶Á³öÊý¾Ý£¬´ý¶Á³ö³¤¶È£¬´ø¶Á³öµØÖ·£¬Æ÷¼þÀàÐÍ£© +// read 1 strings of data (Data array, Readout length, command to be written , SlaveDevice address ). +// len = uint8 (max buffer to read 256bytes) +static int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { + + if ( !data || len == 0 ) + return 0; + + // extra wait 500us (514us measured) + // 200us (xx measured) + SpinDelayUs(600); + bool bBreak = true; + uint16_t readcount = 0; + + do { + if (!I2C_Start()) + return 0; + + // 0xB0 / 0xC0 == i2c write + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + // 0xB1 / 0xC1 == i2c read + I2C_Start(); + I2C_SendByte(device_address | 1); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + if (bBreak) { + I2C_Stop(); + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return 0; + } + + while (len) { + + int16_t tmp = I2C_ReadByte(); + if ( tmp < 0 ) + return tmp; + + *data = (uint8_t)tmp & 0xFF; + + len--; + + // ¶ÁÈ¡µÄµÚÒ»¸ö×Ö½ÚΪºóÐø³¤¶È + // The first byte in response is the message length + if (!readcount && (len > *data)) { + len = *data; + } else { + data++; + } + readcount++; + + // acknowledgements. After last byte send NACK. + if (len == 0) + I2C_NoAck(); + else + I2C_Ack(); + } + + I2C_Stop(); + // return bytecount - first byte (which is length byte) + return --readcount; +} + +static int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { + //START, 0xB0, 0x00, 0x00, START, 0xB1, xx, yy, zz, ......, STOP + bool bBreak = true; + uint8_t readcount = 0; + + // sending + do { + if (!I2C_Start()) + return 0; + + // 0xB0 / 0xC0 i2c write + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + // msb + I2C_SendByte(msb); + if (!I2C_WaitAck()) + break; + + // lsb + I2C_SendByte(lsb); + if (!I2C_WaitAck()) + break; + + // 0xB1 / 0xC1 i2c read + I2C_Start(); + I2C_SendByte(device_address | 1); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + if (bBreak) { + I2C_Stop(); + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return 0; + } + + // reading + while (len) { + + int16_t tmp = I2C_ReadByte(); + if ( tmp < 0 ) + return tmp; + + *data = (uint8_t)tmp & 0xFF; + + data++; + readcount++; + len--; + + // acknowledgements. After last byte send NACK. + if (len == 0) + I2C_NoAck(); + else + I2C_Ack(); + } + + I2C_Stop(); + return readcount; +} + +static bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { + //START, 0xB0, 0x00, 0x00, xx, yy, zz, ......, STOP + bool bBreak = true; + + do { + if (!I2C_Start()) + return false; + + // 0xB0 == i2c write + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + // msb + I2C_SendByte(msb); + if (!I2C_WaitAck()) + break; + + // lsb + I2C_SendByte(lsb); + if (!I2C_WaitAck()) + break; + + while (len) { + I2C_SendByte(*data); + if (!I2C_WaitAck()) + break; + + len--; + data++; + } + + if (len == 0) + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +void I2C_print_status(void) { + DbpString("Smart card module (ISO 7816)"); + uint8_t resp[] = {0,0,0,0}; + I2C_init(); + I2C_Reset_EnterMainProgram(); + uint8_t len = I2C_BufferRead(resp, sizeof(resp), I2C_DEVICE_CMD_GETVERSION, I2C_DEVICE_ADDRESS_MAIN); + if ( len > 0 ) + Dbprintf(" version.................v%x.%02x", resp[0], resp[1]); + else + DbpString(" version.................FAILED"); +} + +// Will read response from smart card module, retries 3 times to get the data. +static bool sc_rx_bytes(uint8_t* dest, uint8_t *destlen) { + uint8_t i = 3; + int16_t len = 0; + while (i--) { + + I2C_WaitForSim(); + + len = I2C_BufferRead(dest, *destlen, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + + if ( len > 1 ){ + break; + } else if ( len == 1 ) { + continue; + } else if ( len <= 0 ) { + return false; + } + } + // after three + if ( len <= 1 ) + return false; + + *destlen = (uint8_t)len & 0xFF; + return true; +} + +static bool GetATR(smart_card_atr_t *card_ptr) { + + if ( !card_ptr ) { + return false; + } + + card_ptr->atr_len = 0; + memset(card_ptr->atr, 0, sizeof(card_ptr->atr)); + + // Send ATR + // start [C0 01] stop start C1 len aa bb cc stop] + I2C_WriteCmd(I2C_DEVICE_CMD_GENERATE_ATR, I2C_DEVICE_ADDRESS_MAIN); + uint8_t cmd[1] = {1}; + LogTrace(cmd, 1, 0, 0, NULL, true); + + // wait for sim card to answer. + // 1byte = 1ms, max frame 256bytes. Should wait 256ms at least just in case. + if (!I2C_WaitForSim()) + return false; + + // read bytes from module + uint8_t len = sizeof(card_ptr->atr); + if ( !sc_rx_bytes(card_ptr->atr, &len) ) + return false; + + uint8_t pos_td = 1; + if ( (card_ptr->atr[1] & 0x10) == 0x10) pos_td++; + if ( (card_ptr->atr[1] & 0x20) == 0x20) pos_td++; + if ( (card_ptr->atr[1] & 0x40) == 0x40) pos_td++; + + // T0 indicate presence T=0 vs T=1. T=1 has checksum TCK + if ( (card_ptr->atr[1] & 0x80) == 0x80) { + + pos_td++; + + // 1 == T1 , presence of checksum TCK + if ( (card_ptr->atr[pos_td] & 0x01) == 0x01) { + uint8_t chksum = 0; + // xor property. will be zero when xored with chksum. + for (uint8_t i = 1; i < len; ++i) + chksum ^= card_ptr->atr[i]; + if ( chksum ) { + if ( MF_DBGLEVEL > 2) DbpString("Wrong ATR checksum"); + } + } + } + + // for some reason we only get first byte of atr, if that is so, send dummy command to retrieve the rest of the atr + if (len == 1) { + + uint8_t data[1] = {0}; + I2C_BufferWrite(data, len, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN); + + if ( !I2C_WaitForSim() ) + return false; + + uint8_t len2 = I2C_BufferRead(card_ptr->atr + len, sizeof(card_ptr->atr) - len, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + len = len + len2; + } + + card_ptr->atr_len = len; + LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); + + return true; +} + +void SmartCardAtr(void) { + smart_card_atr_t card; + LED_D_ON(); + clear_trace(); + set_tracing(true); + I2C_init(); + I2C_Reset_EnterMainProgram(); + bool isOK = GetATR( &card ); + cmd_send(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); + set_tracing(false); + LEDsoff(); +} + +void SmartCardRaw( uint64_t arg0, uint64_t arg1, uint8_t *data ) { + + LED_D_ON(); + + uint8_t len = 0; + uint8_t *resp = BigBuf_malloc(ISO7618_MAX_FRAME); + smartcard_command_t flags = arg0; + + if ((flags & SC_CONNECT)) + clear_trace(); + + set_tracing(true); + + if ((flags & SC_CONNECT)) { + + I2C_init(); + I2C_Reset_EnterMainProgram(); + + if ( !(flags & SC_NO_SELECT) ) { + smart_card_atr_t card; + bool gotATR = GetATR( &card ); + //cmd_send(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); + if ( !gotATR ) + goto OUT; + } + } + + if ((flags & SC_RAW)) { + + LogTrace(data, arg1, 0, 0, NULL, true); + + // Send raw bytes + // asBytes = A0 A4 00 00 02 + // arg1 = len 5 + I2C_BufferWrite(data, arg1, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN); + + if ( !I2C_WaitForSim() ) + goto OUT; + + // read bytes from module + len = ISO7618_MAX_FRAME; + sc_rx_bytes(resp, &len); + LogTrace(resp, len, 0, 0, NULL, false); + } +OUT: + cmd_send(CMD_ACK, len, 0, 0, resp, len); + set_tracing(false); + LEDsoff(); +} + +void SmartCardUpgrade(uint64_t arg0) { + + LED_C_ON(); + + #define I2C_BLOCK_SIZE 128 + // write. Sector0, with 11,22,33,44 + // erase is 128bytes, and takes 50ms to execute + + I2C_init(); + I2C_Reset_EnterBootloader(); + + bool isOK = true; + int16_t res = 0; + uint16_t length = arg0; + uint16_t pos = 0; + uint8_t *fwdata = BigBuf_get_addr(); + uint8_t *verfiydata = BigBuf_malloc(I2C_BLOCK_SIZE); + + while (length) { + + uint8_t msb = (pos >> 8) & 0xFF; + uint8_t lsb = pos & 0xFF; + + Dbprintf("FW %02X%02X", msb, lsb); + + size_t size = MIN(I2C_BLOCK_SIZE, length); + + // write + res = I2C_WriteFW(fwdata+pos, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT); + if ( !res ) { + DbpString("Writing failed"); + isOK = false; + break; + } + + // writing takes time. + SpinDelay(50); + + // read + res = I2C_ReadFW(verfiydata, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT); + if ( res <= 0) { + DbpString("Reading back failed"); + isOK = false; + break; + } + + // cmp + if ( 0 != memcmp(fwdata+pos, verfiydata, size)) { + DbpString("not equal data"); + isOK = false; + break; + } + + length -= size; + pos += size; + } + cmd_send(CMD_ACK, isOK, pos, 0, 0, 0); + LED_C_OFF(); +} + +// unfinished (or not needed?) +//void SmartCardSetBaud(uint64_t arg0) { +//} + +void SmartCardSetClock(uint64_t arg0) { + LED_D_ON(); + set_tracing(true); + I2C_init(); + I2C_Reset_EnterMainProgram(); + + // Send SIM CLC + // start [C0 05 xx] stop + I2C_WriteByte(arg0, I2C_DEVICE_CMD_SIM_CLC, I2C_DEVICE_ADDRESS_MAIN); + + cmd_send(CMD_ACK, 1, 0, 0, 0, 0); + set_tracing(false); + LEDsoff(); +} + +#endif \ No newline at end of file diff --git a/armsrc/i2c.h b/armsrc/i2c.h new file mode 100644 index 00000000..a10ac74f --- /dev/null +++ b/armsrc/i2c.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Willok, June 2018 +// Edits by Iceman, July 2018 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// The main i2c code, for communications with smart card module +//----------------------------------------------------------------------------- +#ifndef __I2C_H +#define __I2C_H + +#include +#include + +#define I2C_DEVICE_ADDRESS_BOOT 0xB0 +#define I2C_DEVICE_ADDRESS_MAIN 0xC0 + +#define I2C_DEVICE_CMD_GENERATE_ATR 0x01 +#define I2C_DEVICE_CMD_SEND 0x02 +#define I2C_DEVICE_CMD_READ 0x03 +#define I2C_DEVICE_CMD_SETBAUD 0x04 +#define I2C_DEVICE_CMD_SIM_CLC 0x05 +#define I2C_DEVICE_CMD_GETVERSION 0x06 + +bool I2C_is_available(void); + +#ifdef WITH_SMARTCARD +void SmartCardAtr(void); +void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data); +void SmartCardUpgrade(uint64_t arg0); +void SmartCardSetClock(uint64_t arg0); +void I2C_print_status(void); +#endif + +#endif // __I2C_H diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 32f9594b..1591a062 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -42,6 +42,7 @@ #include "string.h" #include "common.h" #include "cmd.h" +#include "iso14443a.h" // Needed for CRC in emulation mode; // same construction as in ISO 14443; // different initial value (CRC_ICLASS) @@ -90,7 +91,7 @@ static RAMFUNC int OutOfNDecoding(int bit) if(!Uart.bitBuffer) { Uart.bitBuffer = bit ^ 0xFF0; - return FALSE; + return false; } else { Uart.bitBuffer <<= 4; @@ -101,7 +102,7 @@ static RAMFUNC int OutOfNDecoding(int bit) Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; Uart.byteCnt++; Uart.swapper = 0; - if(Uart.byteCnt > 15) { return TRUE; } + if(Uart.byteCnt > 15) { return true; } } else { Uart.swapper = 1; @@ -138,12 +139,12 @@ static RAMFUNC int OutOfNDecoding(int bit) Uart.highCnt = 0; if(Uart.byteCnt == 0) { // Its not straightforward to show single EOFs - // So just leave it and do not return TRUE + // So just leave it and do not return true Uart.output[0] = 0xf0; Uart.byteCnt++; } else { - return TRUE; + return true; } } else if(Uart.state != STATE_START_OF_COMMUNICATION) { @@ -262,7 +263,7 @@ static RAMFUNC int OutOfNDecoding(int bit) Uart.byteCnt++; Uart.output[Uart.byteCnt] = 0xAA; Uart.byteCnt++; - return TRUE; + return true; }*/ } @@ -317,7 +318,7 @@ static RAMFUNC int OutOfNDecoding(int bit) } } - return FALSE; + return false; } //============================================================================= @@ -370,7 +371,7 @@ static RAMFUNC int ManchesterDecoding(int v) if(Demod.buff < 3) { Demod.buff++; - return FALSE; + return false; } if(Demod.state==DEMOD_UNSYNCD) { @@ -472,7 +473,7 @@ static RAMFUNC int ManchesterDecoding(int v) Demod.len++; Demod.state = DEMOD_UNSYNCD; // error = 0x0f; - return TRUE; + return true; } else { Demod.state = DEMOD_ERROR_WAIT; @@ -556,7 +557,7 @@ static RAMFUNC int ManchesterDecoding(int v) } Demod.state = DEMOD_UNSYNCD; - return TRUE; + return true; } else { Demod.output[Demod.len] = 0xad; @@ -611,14 +612,14 @@ static RAMFUNC int ManchesterDecoding(int v) Demod.len++; Demod.output[Demod.len] = 0xBB; Demod.len++; - return TRUE; + return true; } } } // end (state != UNSYNCED) - return FALSE; + return false; } //============================================================================= @@ -638,7 +639,7 @@ void RAMFUNC SnoopIClass(void) // We won't start recording the frames that we acquire until we trigger; // a good trigger condition to get started is probably when we see a // response from the tag. - //int triggered = FALSE; // FALSE to wait first for card + //int triggered = false; // false to wait first for card // The command (reader -> tag) that we're receiving. // The length of a received command will in most cases be no more than 18 bytes. @@ -655,9 +656,9 @@ void RAMFUNC SnoopIClass(void) // The DMA buffer, used to stream samples from the FPGA uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); - set_tracing(TRUE); + set_tracing(true); clear_trace(); - iso14a_set_trigger(FALSE); + iso14a_set_trigger(false); int lastRxCounter; uint8_t *upTo; @@ -675,7 +676,7 @@ void RAMFUNC SnoopIClass(void) Demod.state = DEMOD_UNSYNCD; // Setup for the DMA. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); upTo = dmaBuf; lastRxCounter = DMA_BUFFER_SIZE; FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); @@ -748,12 +749,12 @@ void RAMFUNC SnoopIClass(void) time_stop = (GetCountSspClk()-time_0) << 4; LED_C_ON(); - //if(!LogTrace(Uart.output,Uart.byteCnt, rsamples, Uart.parityBits,TRUE)) break; - //if(!LogTrace(NULL, 0, Uart.endTime*16 - DELAY_READER_AIR2ARM_AS_SNIFFER, 0, TRUE)) break; + //if(!LogTrace(Uart.output,Uart.byteCnt, rsamples, Uart.parityBits,true)) break; + //if(!LogTrace(NULL, 0, Uart.endTime*16 - DELAY_READER_AIR2ARM_AS_SNIFFER, 0, true)) break; if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(Uart.output, Uart.byteCnt, parity); - LogTrace(Uart.output,Uart.byteCnt, time_start, time_stop, parity, TRUE); + LogTrace(Uart.output,Uart.byteCnt, time_start, time_stop, parity, true); } @@ -781,7 +782,7 @@ void RAMFUNC SnoopIClass(void) if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(Demod.output, Demod.len, parity); - LogTrace(Demod.output, Demod.len, time_start, time_stop, parity, FALSE); + LogTrace(Demod.output, Demod.len, time_start, time_stop, parity, false); } // And ready to receive another response. @@ -829,7 +830,7 @@ void rotateCSN(uint8_t* originalCSN, uint8_t* rotatedCSN) { //----------------------------------------------------------------------------- // Wait for commands from reader // Stop when button is pressed -// Or return TRUE when command is captured +// Or return true when command is captured //----------------------------------------------------------------------------- static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) { @@ -847,7 +848,7 @@ static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) for(;;) { WDT_HIT(); - if(BUTTON_PRESS()) return FALSE; + if(BUTTON_PRESS()) return false; if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x00; @@ -857,7 +858,7 @@ static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) if(OutOfNDecoding(b & 0x0f)) { *len = Uart.byteCnt; - return TRUE; + return true; } } } @@ -992,7 +993,7 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Enable and clear the trace - set_tracing(TRUE); + set_tracing(true); clear_trace(); //Use the emulator memory for SIM uint8_t *emulator = BigBuf_get_EM_addr(); @@ -1162,7 +1163,7 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) StartCountSspClk(); // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // To control where we are in the protocol int cmdsRecvd = 0; @@ -1324,11 +1325,11 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(receivedCmd, len, parity); - LogTrace(receivedCmd,len, (r2t_time-time_0)<< 4, (r2t_time-time_0) << 4, parity, TRUE); + LogTrace(receivedCmd,len, (r2t_time-time_0)<< 4, (r2t_time-time_0) << 4, parity, true); if (trace_data != NULL) { GetParity(trace_data, trace_data_size, parity); - LogTrace(trace_data, trace_data_size, (t2r_time-time_0) << 4, (t2r_time-time_0) << 4, parity, FALSE); + LogTrace(trace_data, trace_data_size, (t2r_time-time_0) << 4, (t2r_time-time_0) << 4, parity, false); } if(!tracing) { DbpString("Trace full"); @@ -1359,7 +1360,7 @@ static int SendIClassAnswer(uint8_t *resp, int respLen, int delay) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR|FPGA_HF_SIMULATOR_MODULATE_424K_8BIT); AT91C_BASE_SSC->SSC_THR = 0x00; - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); while(!BUTTON_PRESS()) { if((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)){ b = AT91C_BASE_SSC->SSC_RHR; (void) b; @@ -1397,7 +1398,7 @@ static void TransmitIClassCommand(const uint8_t *cmd, int len, int *samples, int int c; FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); AT91C_BASE_SSC->SSC_THR = 0x00; - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); if (wait) { @@ -1419,7 +1420,7 @@ static void TransmitIClassCommand(const uint8_t *cmd, int len, int *samples, int uint8_t sendbyte; - bool firstpart = TRUE; + bool firstpart = true; c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { @@ -1511,14 +1512,14 @@ void ReaderTransmitIClass(uint8_t* frame, int len) if (tracing) { uint8_t par[MAX_PARITY_SIZE]; GetParity(frame, len, par); - LogTrace(frame, len, rsamples, rsamples, par, TRUE); + LogTrace(frame, len, rsamples, rsamples, par, true); } } //----------------------------------------------------------------------------- // Wait a certain time for tag response -// If a response is captured return TRUE -// If it takes too long return FALSE +// If a response is captured return true +// If it takes too long return false //----------------------------------------------------------------------------- static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) //uint8_t *buffer { @@ -1537,27 +1538,27 @@ static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, uint8_t b; if (elapsed) *elapsed = 0; - bool skip = FALSE; + bool skip = false; c = 0; for(;;) { WDT_HIT(); - if(BUTTON_PRESS()) return FALSE; + if(BUTTON_PRESS()) return false; if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x00; // To make use of exact timing of next command from reader!! if (elapsed) (*elapsed)++; } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - if(c < timeout) { c++; } else { return FALSE; } + if(c < timeout) { c++; } else { return false; } b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; skip = !skip; if(skip) continue; if(ManchesterDecoding(b & 0x0f)) { *samples = c << 3; - return TRUE; + return true; } } } @@ -1566,14 +1567,14 @@ static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, int ReaderReceiveIClass(uint8_t* receivedAnswer) { int samples = 0; - if (!GetIClassAnswer(receivedAnswer,160,&samples,0)) return FALSE; + if (!GetIClassAnswer(receivedAnswer,160,&samples,0)) return false; rsamples += samples; if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(receivedAnswer, Demod.len, parity); - LogTrace(receivedAnswer,Demod.len,rsamples,rsamples,parity,FALSE); + LogTrace(receivedAnswer,Demod.len,rsamples,rsamples,parity,false); } - if(samples == 0) return FALSE; + if(samples == 0) return false; return Demod.len; } @@ -1581,11 +1582,11 @@ void setupIclassReader() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Reset trace buffer - set_tracing(TRUE); + set_tracing(true); clear_trace(); // Setup SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // Start from off (no field generated) // Signal field is off with the appropriate LED LED_D_OFF(); @@ -1821,7 +1822,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { uint8_t resp[ICLASS_BUFFER_SIZE]; setupIclassReader(); - set_tracing(TRUE); + set_tracing(true); while(!BUTTON_PRESS()) { diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 29b23833..059db71e 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -12,10 +12,11 @@ #include "iso14443a.h" +#include +#include #include "proxmark3.h" #include "apps.h" #include "util.h" -#include "string.h" #include "cmd.h" #include "iso14443crc.h" #include "crapto1/crapto1.h" @@ -80,6 +81,8 @@ typedef struct { } tUart; static uint32_t iso14a_timeout; +#define MAX_ISO14A_TIMEOUT 524288 + int rsamples = 0; uint8_t trigger = 0; // the block number for the ISO14443-4 PCB @@ -132,13 +135,13 @@ uint16_t FpgaSendQueueDelay; #define DELAY_FPGA_QUEUE (FpgaSendQueueDelay<<1) // When the PM acts as tag and is sending, it takes -// 4*16 ticks until we can write data to the sending hold register +// 4*16 + 8 ticks until we can write data to the sending hold register // 8*16 ticks until the SHR is transferred to the Sending Shift Register -// 8 ticks until the first transfer starts -// 8 ticks later the FPGA samples the data -// + a varying number of ticks in the FPGA Delay Queue (mod_sig_buf) +// 8 ticks later the FPGA samples the first data +// + 16 ticks until assigned to mod_sig // + 1 tick to assign mod_sig_coil -#define DELAY_ARM2AIR_AS_TAG (4*16 + 8*16 + 8 + 8 + DELAY_FPGA_QUEUE + 1) +// + a varying number of ticks in the FPGA Delay Queue (mod_sig_buf) +#define DELAY_ARM2AIR_AS_TAG (4*16 + 8 + 8*16 + 8 + 16 + 1 + DELAY_FPGA_QUEUE) // When the PM acts as sniffer and is receiving tag data, it takes // 3 ticks A/D conversion @@ -186,33 +189,16 @@ void iso14a_set_trigger(bool enable) { void iso14a_set_timeout(uint32_t timeout) { - iso14a_timeout = timeout; - if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", iso14a_timeout, iso14a_timeout / 106); + // adjust timeout by FPGA delays and 2 additional ssp_frames to detect SOF + iso14a_timeout = timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) + 2; + if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443A Timeout set to %ld (%dms)", timeout, timeout / 106); } -void iso14a_set_ATS_timeout(uint8_t *ats) { - - uint8_t tb1; - uint8_t fwi; - uint32_t fwt; - - if (ats[0] > 1) { // there is a format byte T0 - if ((ats[1] & 0x20) == 0x20) { // there is an interface byte TB(1) - if ((ats[1] & 0x10) == 0x10) { // there is an interface byte TA(1) preceding TB(1) - tb1 = ats[3]; - } else { - tb1 = ats[2]; - } - fwi = (tb1 & 0xf0) >> 4; // frame waiting indicator (FWI) - fwt = 256 * 16 * (1 << fwi); // frame waiting time (FWT) in 1/fc - - iso14a_set_timeout(fwt/(8*16)); - } - } +uint32_t iso14a_get_timeout(void) { + return iso14a_timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) - 2; } - //----------------------------------------------------------------------------- // Generate the parity value for a byte sequence // @@ -246,7 +232,7 @@ void AppendCrc14443a(uint8_t* data, int len) ComputeCrc14443(CRC_14443_A,data,len,data+len,data+len+1); } -void AppendCrc14443b(uint8_t* data, int len) +static void AppendCrc14443b(uint8_t* data, int len) { ComputeCrc14443(CRC_14443_B,data,len,data+len,data+len+1); } @@ -283,7 +269,7 @@ const bool Mod_Miller_LUT[] = { #define IsMillerModulationNibble1(b) (Mod_Miller_LUT[(b & 0x000000F0) >> 4]) #define IsMillerModulationNibble2(b) (Mod_Miller_LUT[(b & 0x0000000F)]) -void UartReset() +static void UartReset() { Uart.state = STATE_UNSYNCD; Uart.bitCount = 0; @@ -295,7 +281,7 @@ void UartReset() Uart.endTime = 0; } -void UartInit(uint8_t *data, uint8_t *parity) +static void UartInit(uint8_t *data, uint8_t *parity) { Uart.output = data; Uart.parity = parity; @@ -455,7 +441,7 @@ const bool Mod_Manchester_LUT[] = { #define IsManchesterModulationNibble2(b) (Mod_Manchester_LUT[(b & 0x000F)]) -void DemodReset() +static void DemodReset() { Demod.state = DEMOD_UNSYNCD; Demod.len = 0; // number of decoded data bytes @@ -469,7 +455,7 @@ void DemodReset() Demod.endTime = 0; } -void DemodInit(uint8_t *data, uint8_t *parity) +static void DemodInit(uint8_t *data, uint8_t *parity) { Demod.output = data; Demod.parity = parity; @@ -793,14 +779,6 @@ static void CodeIso14443aAsTagPar(const uint8_t *cmd, uint16_t len, uint8_t *par ToSendMax++; } -static void CodeIso14443aAsTag(const uint8_t *cmd, uint16_t len) -{ - uint8_t par[MAX_PARITY_SIZE]; - - GetParity(cmd, len, par); - CodeIso14443aAsTagPar(cmd, len, par); -} - static void Code4bitAnswerAsTag(uint8_t cmd) { @@ -840,6 +818,38 @@ static void Code4bitAnswerAsTag(uint8_t cmd) ToSendMax++; } + +static uint8_t *LastReaderTraceTime = NULL; + +static void EmLogTraceReader(void) { + // remember last reader trace start to fix timing info later + LastReaderTraceTime = BigBuf_get_addr() + BigBuf_get_traceLen(); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); +} + + +static void FixLastReaderTraceTime(uint32_t tag_StartTime) { + uint32_t reader_EndTime = Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG; + uint32_t reader_StartTime = Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG; + uint16_t reader_modlen = reader_EndTime - reader_StartTime; + uint16_t approx_fdt = tag_StartTime - reader_EndTime; + uint16_t exact_fdt = (approx_fdt - 20 + 32)/64 * 64 + 20; + reader_StartTime = tag_StartTime - exact_fdt - reader_modlen; + LastReaderTraceTime[0] = (reader_StartTime >> 0) & 0xff; + LastReaderTraceTime[1] = (reader_StartTime >> 8) & 0xff; + LastReaderTraceTime[2] = (reader_StartTime >> 16) & 0xff; + LastReaderTraceTime[3] = (reader_StartTime >> 24) & 0xff; +} + + +static void EmLogTraceTag(uint8_t *tag_data, uint16_t tag_len, uint8_t *tag_Parity, uint32_t ProxToAirDuration) { + uint32_t tag_StartTime = LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG; + uint32_t tag_EndTime = (LastTimeProxToAirStart + ProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG; + LogTrace(tag_data, tag_len, tag_StartTime, tag_EndTime, tag_Parity, false); + FixLastReaderTraceTime(tag_StartTime); +} + + //----------------------------------------------------------------------------- // Wait for commands from reader // Stop when button is pressed @@ -868,33 +878,22 @@ static int GetIso14443aCommandFromReader(uint8_t *received, uint8_t *parity, int b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; if(MillerDecoding(b, 0)) { *len = Uart.len; + EmLogTraceReader(); return true; } } } } -static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNeeded); -int EmSend4bitEx(uint8_t resp, bool correctionNeeded); + +static int EmSend4bitEx(uint8_t resp); int EmSend4bit(uint8_t resp); -int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, bool correctionNeeded, uint8_t *par); -int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool correctionNeeded); -int EmSendCmd(uint8_t *resp, uint16_t respLen); -int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par); -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); +static int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, uint8_t *par); +int EmSendCmdEx(uint8_t *resp, uint16_t respLen); +int EmSendPrecompiledCmd(tag_response_info_t *response_info); -static uint8_t* free_buffer_pointer; -typedef struct { - uint8_t* response; - size_t response_n; - uint8_t* modulation; - size_t modulation_n; - uint32_t ProxToAirDuration; -} tag_response_info_t; - -bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffer_size) { +static bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffer_size) { // Example response, answer to MIFARE Classic read block will be 16 bytes + 2 CRC = 18 bytes // This will need the following byte array for a modulation sequence // 144 data bits (18 * 8) @@ -908,17 +907,18 @@ bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffe // Prepare the tag modulation bits from the message - CodeIso14443aAsTag(response_info->response,response_info->response_n); + GetParity(response_info->response, response_info->response_n, &(response_info->par)); + CodeIso14443aAsTagPar(response_info->response,response_info->response_n, &(response_info->par)); // Make sure we do not exceed the free buffer space if (ToSendMax > max_buffer_size) { Dbprintf("Out of memory, when modulating bits for tag answer:"); - Dbhexdump(response_info->response_n,response_info->response,false); + Dbhexdump(response_info->response_n, response_info->response, false); return false; } // Copy the byte array, used for this modulation to the buffer position - memcpy(response_info->modulation,ToSend,ToSendMax); + memcpy(response_info->modulation, ToSend, ToSendMax); // Store the number of bytes that were used for encoding/modulation and the time needed to transfer them response_info->modulation_n = ToSendMax; @@ -930,21 +930,20 @@ bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffe // "precompile" responses. There are 7 predefined responses with a total of 28 bytes data to transmit. // Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) -// 28 * 8 data bits, 28 * 1 parity bits, 7 start bits, 7 stop bits, 7 correction bits +// 28 * 8 data bits, 28 * 1 parity bits, 7 start bits, 7 stop bits, 7 correction bits for the modulation // -> need 273 bytes buffer #define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 273 -bool prepare_allocated_tag_modulation(tag_response_info_t* response_info) { +bool prepare_allocated_tag_modulation(tag_response_info_t* response_info, uint8_t **buffer, size_t *max_buffer_size) { + // Retrieve and store the current buffer index - response_info->modulation = free_buffer_pointer; - - // Determine the maximum size we can use from our buffer - size_t max_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; + response_info->modulation = *buffer; // Forward the prepare tag modulation function to the inner function - if (prepare_tag_modulation(response_info, max_buffer_size)) { - // Update the free buffer offset - free_buffer_pointer += ToSendMax; + if (prepare_tag_modulation(response_info, *max_buffer_size)) { + // Update the free buffer offset and the remaining buffer size + *buffer += ToSendMax; + *max_buffer_size -= ToSendMax; return true; } else { return false; @@ -1074,8 +1073,8 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) // allocate buffers: uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE); - free_buffer_pointer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); - + uint8_t *free_buffer_pointer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); + size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; // clear trace clear_trace(); set_tracing(true); @@ -1083,7 +1082,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) // Prepare the responses of the anticollision phase // there will be not enough time to do this at the moment the reader sends it REQA for (size_t i=0; imodulation, p_response->modulation_n, receivedCmd[0] == 0x52); - // do the tracing for the previous reader request and this tag answer: - uint8_t par[MAX_PARITY_SIZE]; - GetParity(p_response->response, p_response->response_n, par); - - EmLogTrace(Uart.output, - Uart.len, - Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.parity, - p_response->response, - p_response->response_n, - LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, - (LastTimeProxToAirStart + p_response->ProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, - par); + EmSendPrecompiledCmd(p_response); } if (!tracing) { @@ -1262,7 +1234,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) // prepare a delayed transfer. This simply shifts ToSend[] by a number // of bits specified in the delay parameter. -void PrepareDelayedTransfer(uint16_t delay) +static void PrepareDelayedTransfer(uint16_t delay) { uint8_t bitmask = 0; uint8_t bits_to_shift = 0; @@ -1288,7 +1260,7 @@ void PrepareDelayedTransfer(uint16_t delay) // Transmit the command (to the tag) that was placed in ToSend[]. // Parameter timing: // if NULL: transfer at next possible time, taking into account -// request guard time and frame delay time +// request guard time, startup frame guard time and frame delay time // if == 0: transfer immediately and return time of transfer // if != 0: delay transfer until time specified //------------------------------------------------------------------------------------- @@ -1335,7 +1307,7 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing //----------------------------------------------------------------------------- // Prepare reader command (in bits, support short frames) to send to FPGA //----------------------------------------------------------------------------- -void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8_t *parity) +static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8_t *parity) { int i, j; int last; @@ -1413,21 +1385,13 @@ void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8 ToSendMax++; } -//----------------------------------------------------------------------------- -// Prepare reader command to send to FPGA -//----------------------------------------------------------------------------- -void CodeIso14443aAsReaderPar(const uint8_t *cmd, uint16_t len, const uint8_t *parity) -{ - CodeIso14443aBitsAsReaderPar(cmd, len*8, parity); -} - //----------------------------------------------------------------------------- // Wait for commands from reader // Stop when button is pressed (return 1) or field was gone (return 2) // Or return 0 when command is captured //----------------------------------------------------------------------------- -static int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) +int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) { *len = 0; @@ -1435,40 +1399,45 @@ static int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) int analogCnt = 0; int analogAVG = 0; - // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen - // only, since we are receiving, not transmitting). - // Signal field is off with the appropriate LED - LED_D_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - // Set ADC to read field strength AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; AT91C_BASE_ADC->ADC_MR = ADC_MODE_PRESCALE(63) | ADC_MODE_STARTUP_TIME(1) | ADC_MODE_SAMPLE_HOLD_TIME(15); - AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF); + AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF_LOW); // start ADC AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; - // Now run a 'software UART' on the stream of incoming samples. + // Run a 'software UART' on the stream of incoming samples. UartInit(received, parity); - // Clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - + // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN + do { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = SEC_F; + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; (void) b; + } + } while (GetCountSspClk() < LastTimeProxToAirStart + LastProxToAirDuration + (FpgaSendQueueDelay>>3)); + + // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen + // only, since we are receiving, not transmitting). + // Signal field is off with the appropriate LED + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); + for(;;) { WDT_HIT(); if (BUTTON_PRESS()) return 1; // test if the field exists - if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF)) { + if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF_LOW)) { analogCnt++; - analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF]; + analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF_LOW]; AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; if (analogCnt >= 32) { - if ((MAX_ADC_HF_VOLTAGE * (analogAVG / analogCnt) >> 10) < MF_MINFIELDV) { + if ((MAX_ADC_HF_VOLTAGE_LOW * (analogAVG / analogCnt) >> 10) < MF_MINFIELDV) { vtime = GetTickCount(); if (!timer) timer = vtime; // 50ms no field --> card to idle state @@ -1482,9 +1451,10 @@ static int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) // receive and test the miller decoding if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; if(MillerDecoding(b, 0)) { *len = Uart.len; + EmLogTraceReader(); return 0; } } @@ -1493,19 +1463,27 @@ static int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) } -static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNeeded) +static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen) { uint8_t b; uint16_t i = 0; - uint32_t ThisTransferTime; - + bool correctionNeeded; + // Modulate Manchester FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD); // include correction bit if necessary - if (Uart.parityBits & 0x01) { - correctionNeeded = true; + if (Uart.bitCount == 7) + { + // Short tags (7 bits) don't have parity, determine the correct value from MSB + correctionNeeded = Uart.output[0] & 0x40; } + else + { + // Look at the last parity bit + correctionNeeded = Uart.parity[(Uart.len-1)/8] & (0x80 >> ((Uart.len-1) & 7)); + } + if(correctionNeeded) { // 1236, so correction bit needed i = 0; @@ -1525,10 +1503,7 @@ static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNe if (AT91C_BASE_SSC->SSC_RHR) break; } - while ((ThisTransferTime = GetCountSspClk()) & 0x00000007); - - // Clear TXRDY: - AT91C_BASE_SSC->SSC_THR = SEC_F; + LastTimeProxToAirStart = (GetCountSspClk() & 0xfffffff8) + (correctionNeeded?8:0); // send cycle for(; i < respLen; ) { @@ -1542,97 +1517,60 @@ static int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen, bool correctionNe } } - // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN again: - uint8_t fpga_queued_bits = FpgaSendQueueDelay >> 3; - for (i = 0; i <= fpga_queued_bits/8 + 1; ) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = SEC_F; - FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - i++; - } - } - - LastTimeProxToAirStart = ThisTransferTime + (correctionNeeded?8:0); - return 0; } -int EmSend4bitEx(uint8_t resp, bool correctionNeeded){ + +static int EmSend4bitEx(uint8_t resp){ Code4bitAnswerAsTag(resp); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax, correctionNeeded); + int res = EmSendCmd14443aRaw(ToSend, ToSendMax); // do the tracing for the previous reader request and this tag answer: - uint8_t par[1]; - GetParity(&resp, 1, par); - EmLogTrace(Uart.output, - Uart.len, - Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.parity, - &resp, - 1, - LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, - (LastTimeProxToAirStart + LastProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, - par); + EmLogTraceTag(&resp, 1, NULL, LastProxToAirDuration); return res; } + int EmSend4bit(uint8_t resp){ - return EmSend4bitEx(resp, false); + return EmSend4bitEx(resp); } -int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, bool correctionNeeded, uint8_t *par){ + +static int EmSendCmdExPar(uint8_t *resp, uint16_t respLen, uint8_t *par){ CodeIso14443aAsTagPar(resp, respLen, par); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax, correctionNeeded); + int res = EmSendCmd14443aRaw(ToSend, ToSendMax); // do the tracing for the previous reader request and this tag answer: - EmLogTrace(Uart.output, - Uart.len, - Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.parity, - resp, - respLen, - LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, - (LastTimeProxToAirStart + LastProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, - par); + EmLogTraceTag(resp, respLen, par, LastProxToAirDuration); return res; } -int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool correctionNeeded){ + +int EmSendCmdEx(uint8_t *resp, uint16_t respLen){ uint8_t par[MAX_PARITY_SIZE]; GetParity(resp, respLen, par); - return EmSendCmdExPar(resp, respLen, correctionNeeded, par); + return EmSendCmdExPar(resp, respLen, par); } + int EmSendCmd(uint8_t *resp, uint16_t respLen){ uint8_t par[MAX_PARITY_SIZE]; GetParity(resp, respLen, par); - return EmSendCmdExPar(resp, respLen, false, par); + return EmSendCmdExPar(resp, respLen, par); } + int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par){ - return EmSendCmdExPar(resp, respLen, false, par); + return EmSendCmdExPar(resp, respLen, par); } -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) -{ - if (tracing) { - // 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. - // with n >= 9. The start of the tags answer can be measured and therefore the end of the received command be calculated: - uint16_t reader_modlen = reader_EndTime - reader_StartTime; - uint16_t approx_fdt = tag_StartTime - reader_EndTime; - uint16_t exact_fdt = (approx_fdt - 20 + 32)/64 * 64 + 20; - reader_EndTime = tag_StartTime - exact_fdt; - reader_StartTime = reader_EndTime - reader_modlen; - if (!LogTrace(reader_data, reader_len, reader_StartTime, reader_EndTime, reader_Parity, true)) { - return false; - } else return(!LogTrace(tag_data, tag_len, tag_StartTime, tag_EndTime, tag_Parity, false)); - } else { - return true; - } + +int EmSendPrecompiledCmd(tag_response_info_t *response_info) { + int ret = EmSendCmd14443aRaw(response_info->modulation, response_info->modulation_n); + // do the tracing for the previous reader request and this tag answer: + EmLogTraceTag(response_info->response, response_info->response_n, &(response_info->par), response_info->ProxToAirDuration); + return ret; } + //----------------------------------------------------------------------------- // Wait a certain time for tag response // If a response is captured return true @@ -1693,7 +1631,7 @@ void ReaderTransmitPar(uint8_t* frame, uint16_t len, uint8_t *par, uint32_t *tim } -void ReaderTransmitBits(uint8_t* frame, uint16_t len, uint32_t *timing) +static void ReaderTransmitBits(uint8_t* frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect uint8_t par[MAX_PARITY_SIZE]; @@ -1710,7 +1648,8 @@ void ReaderTransmit(uint8_t* frame, uint16_t len, uint32_t *timing) ReaderTransmitBitsPar(frame, len*8, par, timing); } -int ReaderReceiveOffset(uint8_t* receivedAnswer, uint16_t offset, uint8_t *parity) + +static int ReaderReceiveOffset(uint8_t* receivedAnswer, uint16_t offset, uint8_t *parity) { if (!GetIso14443aAnswerFromTag(receivedAnswer, parity, offset)) return false; if (tracing) { @@ -1719,6 +1658,7 @@ int ReaderReceiveOffset(uint8_t* receivedAnswer, uint16_t offset, uint8_t *parit return Demod.len; } + int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) { if (!GetIso14443aAnswerFromTag(receivedAnswer, parity, 0)) return false; @@ -1728,13 +1668,66 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) return Demod.len; } + +static void iso14a_set_ATS_times(uint8_t *ats) { + + uint8_t tb1; + uint8_t fwi, sfgi; + uint32_t fwt, sfgt; + + if (ats[0] > 1) { // there is a format byte T0 + if ((ats[1] & 0x20) == 0x20) { // there is an interface byte TB(1) + if ((ats[1] & 0x10) == 0x10) { // there is an interface byte TA(1) preceding TB(1) + tb1 = ats[3]; + } else { + tb1 = ats[2]; + } + fwi = (tb1 & 0xf0) >> 4; // frame waiting time integer (FWI) + if (fwi != 15) { + fwt = 256 * 16 * (1 << fwi); // frame waiting time (FWT) in 1/fc + iso14a_set_timeout(fwt/(8*16)); + } + sfgi = tb1 & 0x0f; // startup frame guard time integer (SFGI) + if (sfgi != 0 && sfgi != 15) { + sfgt = 256 * 16 * (1 << sfgi); // startup frame guard time (SFGT) in 1/fc + NextTransferTime = MAX(NextTransferTime, Demod.endTime + (sfgt - DELAY_AIR2ARM_AS_READER - DELAY_ARM2AIR_AS_READER)/16); + } + } + } +} + + +static int GetATQA(uint8_t *resp, uint8_t *resp_par) { + +#define WUPA_RETRY_TIMEOUT 10 // 10ms + uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP + + uint32_t save_iso14a_timeout = iso14a_get_timeout(); + iso14a_set_timeout(1236/(16*8)+1); // response to WUPA is expected at exactly 1236/fc. No need to wait longer. + + uint32_t start_time = GetTickCount(); + int len; + + // we may need several tries if we did send an unknown command or a wrong authentication before... + do { + // Broadcast for a card, WUPA (0x52) will force response from all cards in the field + ReaderTransmitBitsPar(wupa, 7, NULL, NULL); + // Receive the ATQA + len = ReaderReceive(resp, resp_par); + } while (len == 0 && GetTickCount() <= start_time + WUPA_RETRY_TIMEOUT); + + iso14a_set_timeout(save_iso14a_timeout); + return len; +} + + // performs iso14443a anticollision (optional) and card select procedure // fills the uid and cuid pointer unless NULL // fills the card info record unless NULL // if anticollision is false, then the UID must be provided in uid_ptr[] // and num_cascades must be set (1: 4 Byte UID, 2: 7 Byte UID, 3: 10 Byte UID) -int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades) { - uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP +// requests ATS unless no_rats is true +int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { uint8_t sel_all[] = { 0x93,0x20 }; uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t rats[] = { 0xE0,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0 @@ -1747,16 +1740,19 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u int cascade_level = 0; int len; - // Broadcast for a card, WUPA (0x52) will force response from all cards in the field - ReaderTransmitBitsPar(wupa, 7, NULL, NULL); - - // Receive the ATQA - if(!ReaderReceive(resp, resp_par)) return 0; + // init card struct + if(p_hi14a_card) { + p_hi14a_card->uidlen = 0; + memset(p_hi14a_card->uid, 0, 10); + p_hi14a_card->ats_len = 0; + } + + if (!GetATQA(resp, resp_par)) { + return 0; + } if(p_hi14a_card) { memcpy(p_hi14a_card->atqa, resp, 2); - p_hi14a_card->uidlen = 0; - memset(p_hi14a_card->uid,0,10); } if (anticollision) { @@ -1862,37 +1858,38 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u if(p_hi14a_card) { p_hi14a_card->sak = sak; - p_hi14a_card->ats_len = 0; } - // non iso14443a compliant tag + // PICC compilant with iso14443a-4 ---> (SAK & 0x20 != 0) if( (sak & 0x20) == 0) return 2; - // Request for answer to select - AppendCrc14443a(rats, 2); - ReaderTransmit(rats, sizeof(rats), NULL); + if (!no_rats) { + // Request for answer to select + AppendCrc14443a(rats, 2); + ReaderTransmit(rats, sizeof(rats), NULL); - if (!(len = ReaderReceive(resp, resp_par))) return 0; + if (!(len = ReaderReceive(resp, resp_par))) return 0; - - if(p_hi14a_card) { - memcpy(p_hi14a_card->ats, resp, sizeof(p_hi14a_card->ats)); - p_hi14a_card->ats_len = len; + if(p_hi14a_card) { + memcpy(p_hi14a_card->ats, resp, len); + p_hi14a_card->ats_len = len; + } + + // reset the PCB block number + iso14_pcb_blocknum = 0; + + // set default timeout and delay next transfer based on ATS + iso14a_set_ATS_times(resp); + } - - // reset the PCB block number - iso14_pcb_blocknum = 0; - - // set default timeout based on ATS - iso14a_set_ATS_timeout(resp); - return 1; } + void iso14443a_setup(uint8_t fpga_minor_mode) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1911,37 +1908,98 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { DemodReset(); UartReset(); NextTransferTime = 2*DELAY_ARM2AIR_AS_READER; - iso14a_set_timeout(1050); // 10ms default + iso14a_set_timeout(1060); // 10ms default } +/* Peter Fillmore 2015 +Added card id field to the function + info from ISO14443A standard +b1 = Block Number +b2 = RFU (always 1) +b3 = depends on block +b4 = Card ID following if set to 1 +b5 = depends on block type +b6 = depends on block type +b7,b8 = block type. +Coding of I-BLOCK: +b8 b7 b6 b5 b4 b3 b2 b1 +0 0 0 x x x 1 x +b5 = chaining bit +Coding of R-block: +b8 b7 b6 b5 b4 b3 b2 b1 +1 0 1 x x 0 1 x +b5 = ACK/NACK +Coding of S-block: +b8 b7 b6 b5 b4 b3 b2 b1 +1 1 x x x 0 1 0 +b5,b6 = 00 - DESELECT + 11 - WTX +*/ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { uint8_t parity[MAX_PARITY_SIZE]; - uint8_t real_cmd[cmd_len+4]; - real_cmd[0] = 0x0a; //I-Block + uint8_t real_cmd[cmd_len + 4]; + + // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 + real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) // put block number into the PCB real_cmd[0] |= iso14_pcb_blocknum; - real_cmd[1] = 0x00; //CID: 0 //FIXME: allow multiple selected cards - memcpy(real_cmd+2, cmd, cmd_len); - AppendCrc14443a(real_cmd,cmd_len+2); + memcpy(real_cmd + 1, cmd, cmd_len); + AppendCrc14443a(real_cmd, cmd_len + 1); - ReaderTransmit(real_cmd, cmd_len+4, NULL); + ReaderTransmit(real_cmd, cmd_len + 3, NULL); + size_t len = ReaderReceive(data, parity); uint8_t *data_bytes = (uint8_t *) data; - if (!len) + + if (!len) { return 0; //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 - else if (len >= 4 // PCB+CID+CRC = 4 bytes + } else{ + // S-Block WTX + while((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. + AppendCrc14443a(data_bytes, len - 2); + // transmit S-Block + ReaderTransmit(data_bytes, len, NULL); + // retrieve the result again (with increased timeout) + len = ReaderReceive(data, parity); + 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; - } + { + iso14_pcb_blocknum ^= 1; + } + // crc check + if (len >=3 && !CheckCrc14443(CRC_14443_A, data_bytes, len)) { + return -1; + } + + } + + // cut frame byte + len -= 1; + // memmove(data_bytes, data_bytes + 1, len); + for (int i = 0; i < len; i++) + data_bytes[i] = data_bytes[i + 1]; + return len; } + //----------------------------------------------------------------------------- // Read an ISO 14443a tag. Send out commands and store answers. // @@ -1954,25 +2012,36 @@ void ReaderIso14443a(UsbCommand *c) size_t lenbits = c->arg[1] >> 16; uint32_t timeout = c->arg[2]; uint32_t arg0 = 0; - byte_t buf[USB_CMD_DATA_SIZE]; + byte_t buf[USB_CMD_DATA_SIZE] = {0}; uint8_t par[MAX_PARITY_SIZE]; + bool cantSELECT = false; - if(param & ISO14A_CONNECT) { + set_tracing(true); + + if(param & ISO14A_CLEAR_TRACE) { clear_trace(); } - set_tracing(true); - if(param & ISO14A_REQUEST_TRIGGER) { iso14a_set_trigger(true); } if(param & ISO14A_CONNECT) { + LED_A_ON(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if(!(param & ISO14A_NO_SELECT)) { iso14a_card_select_t *card = (iso14a_card_select_t*)buf; - arg0 = iso14443a_select_card(NULL, card, NULL, true, 0); + arg0 = iso14443a_select_card(NULL, card, NULL, true, 0, param & ISO14A_NO_RATS); + + // if we cant select then we cant send data + if (arg0 != 1 && arg0 != 2) { + // 1 - all is OK with ATS, 2 - without ATS + cantSELECT = true; + } + + LED_B_ON(); cmd_send(CMD_ACK,arg0,card->uidlen,0,buf,sizeof(iso14a_card_select_t)); + LED_B_OFF(); } } @@ -1980,12 +2049,14 @@ void ReaderIso14443a(UsbCommand *c) iso14a_set_timeout(timeout); } - if(param & ISO14A_APDU) { + if(param & ISO14A_APDU && !cantSELECT) { arg0 = iso14_apdu(cmd, len, buf); - cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + LED_B_ON(); + cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + LED_B_OFF(); } - if(param & ISO14A_RAW) { + if(param & ISO14A_RAW && !cantSELECT) { if(param & ISO14A_APPEND_CRC) { if(param & ISO14A_TOPAZMODE) { AppendCrc14443b(cmd,len); @@ -2021,7 +2092,10 @@ void ReaderIso14443a(UsbCommand *c) } } arg0 = ReaderReceive(buf, par); + + LED_B_ON(); cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + LED_B_OFF(); } if(param & ISO14A_REQUEST_TRIGGER) { @@ -2040,7 +2114,7 @@ void ReaderIso14443a(UsbCommand *c) // Determine the distance between two nonces. // Assume that the difference is small, but we don't know which is first. // Therefore try in alternating directions. -int32_t dist_nt(uint32_t nt1, uint32_t nt2) { +static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { uint16_t i; uint32_t nttmp1, nttmp2; @@ -2077,9 +2151,7 @@ void ReaderMifare(bool first_try) uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; - if (first_try) { - iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); - } + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); // free eventually allocated BigBuf memory. We want all for tracing. BigBuf_free(); @@ -2087,9 +2159,9 @@ void ReaderMifare(bool first_try) clear_trace(); set_tracing(true); - byte_t nt_diff = 0; + uint8_t nt_diff = 0; uint8_t par[1] = {0}; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough - static byte_t par_low = 0; + static uint8_t par_low = 0; bool led_on = true; uint8_t uid[10] ={0}; uint32_t cuid; @@ -2097,11 +2169,11 @@ void ReaderMifare(bool first_try) uint32_t nt = 0; uint32_t previous_nt = 0; static uint32_t nt_attacked = 0; - byte_t par_list[8] = {0x00}; - byte_t ks_list[8] = {0x00}; + uint8_t par_list[8] = {0x00}; + uint8_t ks_list[8] = {0x00}; #define PRNG_SEQUENCE_LENGTH (1 << 16); - static uint32_t sync_time; + uint32_t sync_time = GetCountSspClk() & 0xfffffff8; static int32_t sync_cycles; int catch_up_cycles = 0; int last_catch_up = 0; @@ -2111,10 +2183,9 @@ void ReaderMifare(bool first_try) if (first_try) { mf_nr_ar3 = 0; - sync_time = GetCountSspClk() & 0xfffffff8; + par[0] = par_low = 0; sync_cycles = PRNG_SEQUENCE_LENGTH; // theory: Mifare Classic's random generator repeats every 2^16 cycles (and so do the tag nonces). nt_attacked = 0; - par[0] = 0; } else { // we were unsuccessful on a previous call. Try another READER nonce (first 3 parity bits remain the same) @@ -2130,6 +2201,7 @@ void ReaderMifare(bool first_try) #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 SYNC_TIME_BUFFER 16 // if there is only SYNC_TIME_BUFFER left before next planned sync, wait for next PRNG cycle #define NUM_DEBUG_INFOS 8 // per strategy #define MAX_STRATEGY 3 uint16_t unexpected_random = 0; @@ -2168,7 +2240,7 @@ void ReaderMifare(bool first_try) SpinDelay(100); } - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card"); continue; } @@ -2179,8 +2251,8 @@ void ReaderMifare(bool first_try) sync_time = (sync_time & 0xfffffff8) + sync_cycles + catch_up_cycles; catch_up_cycles = 0; - // if we missed the sync time already, advance to the next nonce repeat - while(GetCountSspClk() > sync_time) { + // if we missed the sync time already or are about to miss it, advance to the next nonce repeat + while(sync_time < GetCountSspClk() + SYNC_TIME_BUFFER) { elapsed_prng_sequences++; sync_time = (sync_time & 0xfffffff8) + sync_cycles; } @@ -2336,14 +2408,14 @@ void ReaderMifare(bool first_try) } } - byte_t buf[28]; + uint8_t buf[32]; memcpy(buf + 0, uid, 4); num_to_bytes(nt, 4, buf + 4); memcpy(buf + 8, par_list, 8); memcpy(buf + 16, ks_list, 8); - memcpy(buf + 24, mf_nr_ar, 4); + memcpy(buf + 24, mf_nr_ar, 8); - cmd_send(CMD_ACK, isOK, 0, 0, buf, 28); + cmd_send(CMD_ACK, isOK, 0, 0, buf, 32); // Thats it... FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -2352,696 +2424,6 @@ void ReaderMifare(bool first_try) set_tracing(false); } -/** - *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 - * FLAG_RANDOM_NONCE - means we should generate some pseudo-random nonce data (only allows moebius attack) - *@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) - */ -void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *datain) -{ - int cardSTATE = MFEMUL_NOFIELD; - int _UID_LEN = 0; // 4, 7, 10 - int vHf = 0; // in mV - int res; - uint32_t selTimer = 0; - uint32_t authTimer = 0; - uint16_t len = 0; - uint8_t cardWRBL = 0; - uint8_t cardAUTHSC = 0; - uint8_t cardAUTHKEY = 0xff; // no authentication - uint32_t cardRr = 0; - uint32_t cuid = 0; - //uint32_t rn_enc = 0; - uint32_t ans = 0; - uint32_t cardINTREG = 0; - uint8_t cardINTBLOCK = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - uint32_t numReads = 0;//Counts numer of times reader read a block - uint8_t receivedCmd[MAX_MIFARE_FRAME_SIZE]; - uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE]; - uint8_t response[MAX_MIFARE_FRAME_SIZE]; - uint8_t response_par[MAX_MIFARE_PARITY_SIZE]; - - uint8_t rATQA[] = {0x04, 0x00}; // Mifare classic 1k 4BUID - uint8_t rUIDBCC1[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; - uint8_t rUIDBCC2[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; // !!! - uint8_t rUIDBCC3[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; - - uint8_t rSAKfinal[]= {0x08, 0xb6, 0xdd}; // mifare 1k indicated - uint8_t rSAK1[] = {0x04, 0xda, 0x17}; // indicate UID not finished - - uint8_t rAUTH_NT[] = {0x01, 0x02, 0x03, 0x04}; - uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00}; - - //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) - 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 - uint32_t nonce; - if (flags & FLAG_RANDOM_NONCE) { - nonce = prand(); - } else { - nonce = bytes_to_num(rAUTH_NT, 4); - } - - //-- Determine the UID - // Can be set from emulator memory, incoming data - // and can be 7 or 4 bytes long - if (flags & FLAG_4B_UID_IN_DATA) - { - // 4B uid comes from data-portion of packet - memcpy(rUIDBCC1,datain,4); - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - _UID_LEN = 4; - } else if (flags & FLAG_7B_UID_IN_DATA) { - // 7B uid comes from data-portion of packet - memcpy(&rUIDBCC1[1],datain,3); - memcpy(rUIDBCC2, datain+3, 4); - _UID_LEN = 7; - } else if (flags & FLAG_10B_UID_IN_DATA) { - memcpy(&rUIDBCC1[1], datain, 3); - memcpy(&rUIDBCC2[1], datain+3, 3); - memcpy( rUIDBCC3, datain+6, 4); - _UID_LEN = 10; - } else { - // get UID from emul memory - guess at length - emlGetMemBt(receivedCmd, 7, 1); - if (receivedCmd[0] == 0x00) { // ---------- 4BUID - emlGetMemBt(rUIDBCC1, 0, 4); - _UID_LEN = 4; - } else { // ---------- 7BUID - emlGetMemBt(&rUIDBCC1[1], 0, 3); - emlGetMemBt(rUIDBCC2, 3, 4); - _UID_LEN = 7; - } - } - - switch (_UID_LEN) { - case 4: - // save CUID - cuid = bytes_to_num(rUIDBCC1, 4); - // BCC - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - if (MF_DBGLEVEL >= 2) { - Dbprintf("4B UID: %02x%02x%02x%02x", - rUIDBCC1[0], - rUIDBCC1[1], - rUIDBCC1[2], - rUIDBCC1[3] - ); - } - break; - case 7: - rATQA[0] |= 0x40; - // save CUID - cuid = bytes_to_num(rUIDBCC2, 4); - // CascadeTag, CT - rUIDBCC1[0] = 0x88; - // BCC - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; - if (MF_DBGLEVEL >= 2) { - Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x", - rUIDBCC1[1], - rUIDBCC1[2], - rUIDBCC1[3], - rUIDBCC2[0], - rUIDBCC2[1], - rUIDBCC2[2], - rUIDBCC2[3] - ); - } - break; - case 10: - rATQA[0] |= 0x80; - //sak_10[0] &= 0xFB; - // save CUID - cuid = bytes_to_num(rUIDBCC3, 4); - // CascadeTag, CT - rUIDBCC1[0] = 0x88; - rUIDBCC2[0] = 0x88; - // BCC - 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 (MF_DBGLEVEL >= 2) { - Dbprintf("10B UID: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", - rUIDBCC1[1], - rUIDBCC1[2], - rUIDBCC1[3], - rUIDBCC2[1], - rUIDBCC2[2], - rUIDBCC2[3], - rUIDBCC3[0], - rUIDBCC3[1], - rUIDBCC3[2], - rUIDBCC3[3] - ); - } - break; - default: - break; - } - - // We need to listen to the high-frequency, peak-detected path. - iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); - - // free eventually allocated BigBuf memory but keep Emulator Memory - BigBuf_free_keep_EM(); - - // clear trace - clear_trace(); - set_tracing(true); - - bool finished = false; - bool button_pushed = BUTTON_PRESS(); - while (!button_pushed && !finished && !usb_poll_validate_length()) { - WDT_HIT(); - - // find reader field - if (cardSTATE == MFEMUL_NOFIELD) { - vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; - if (vHf > MF_MINFIELDV) { - cardSTATE_TO_IDLE(); - LED_A_ON(); - } - } - if (cardSTATE == MFEMUL_NOFIELD) { - button_pushed = BUTTON_PRESS(); - continue; - } - - //Now, get data - res = EmGetCmd(receivedCmd, &len, receivedCmd_par); - if (res == 2) { //Field is off! - cardSTATE = MFEMUL_NOFIELD; - LEDsoff(); - continue; - } else if (res == 1) { - break; //return value 1 means button press - } - - // REQ or WUP request in ANY state and WUP in HALTED state - if (len == 1 && ((receivedCmd[0] == ISO14443A_CMD_REQA && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == ISO14443A_CMD_WUPA)) { - selTimer = GetTickCount(); - EmSendCmdEx(rATQA, sizeof(rATQA), (receivedCmd[0] == ISO14443A_CMD_WUPA)); - cardSTATE = MFEMUL_SELECT1; - - // init crypto block - LED_B_OFF(); - LED_C_OFF(); - crypto1_destroy(pcs); - cardAUTHKEY = 0xff; - if (flags & FLAG_RANDOM_NONCE) { - nonce = prand(); - } - continue; - } - - switch (cardSTATE) { - case MFEMUL_NOFIELD: - case MFEMUL_HALTED: - 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); - break; - } - case MFEMUL_SELECT1:{ - // select all - 0x93 0x20 - if (len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x20)) { - if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL received"); - EmSendCmd(rUIDBCC1, sizeof(rUIDBCC1)); - break; - } - - // select card - 0x93 0x70 ... - if (len == 9 && - (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], rUIDBCC1, 4) == 0)) { - if (MF_DBGLEVEL >= 4) - Dbprintf("SELECT %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); - - switch(_UID_LEN) { - case 4: - cardSTATE = MFEMUL_WORK; - LED_B_ON(); - if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol1 time: %d", GetTickCount() - selTimer); - EmSendCmd(rSAKfinal, sizeof(rSAKfinal)); - break; - case 7: - cardSTATE = MFEMUL_SELECT2; - EmSendCmd(rSAK1, sizeof(rSAK1)); - break; - case 10: - cardSTATE = MFEMUL_SELECT2; - EmSendCmd(rSAK1, sizeof(rSAK1)); - break; - default:break; - } - } else { - cardSTATE_TO_IDLE(); - } - break; - } - case MFEMUL_SELECT3:{ - if (!len) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - // select all cl3 - 0x97 0x20 - if (len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3 && receivedCmd[1] == 0x20)) { - EmSendCmd(rUIDBCC3, sizeof(rUIDBCC3)); - break; - } - // select card cl3 - 0x97 0x70 - if (len == 9 && - (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3 && - receivedCmd[1] == 0x70 && - memcmp(&receivedCmd[2], rUIDBCC3, 4) == 0) ) { - - EmSendCmd(rSAKfinal, sizeof(rSAKfinal)); - cardSTATE = MFEMUL_WORK; - LED_B_ON(); - if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol3 time: %d", GetTickCount() - selTimer); - break; - } - cardSTATE_TO_IDLE(); - break; - } - case MFEMUL_AUTH1:{ - if( 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); - break; - } - - uint32_t nr = bytes_to_num(receivedCmd, 4); - uint32_t ar = bytes_to_num(&receivedCmd[4], 4); - - // Collect AR/NR per keytype & sector - if(flags & 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; - if (flags & FLAG_RANDOM_NONCE) { - nonce = prand(); - } else { - 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 OK - if (cardRr != prng_successor(nonce, 64)){ - if (MF_DBGLEVEL >= 2) Dbprintf("AUTH FAILED for sector %d with key %c. cardRr=%08x, succ=%08x", - cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B', - cardRr, prng_successor(nonce, 64)); - // Shouldn't we respond anything here? - // Right now, we don't nack or anything, which causes the - // reader to do a WUPA after a while. /Martin - // -- which is the correct response. /piwi - 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); - break; - } - - //auth successful - ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0); - - num_to_bytes(ans, 4, rAUTH_AT); - // --- crypto - EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); - LED_C_ON(); - cardSTATE = MFEMUL_WORK; - if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED for sector %d with key %c. time=%d", - cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B', - GetTickCount() - authTimer); - break; - } - case MFEMUL_SELECT2:{ - if (!len) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - // select all cl2 - 0x95 0x20 - if (len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x20)) { - EmSendCmd(rUIDBCC2, sizeof(rUIDBCC2)); - break; - } - - // select cl2 card - 0x95 0x70 xxxxxxxxxxxx - if (len == 9 && - (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], rUIDBCC2, 4) == 0)) { - switch(_UID_LEN) { - case 7: - EmSendCmd(rSAKfinal, sizeof(rSAKfinal)); - cardSTATE = MFEMUL_WORK; - LED_B_ON(); - if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol2 time: %d", GetTickCount() - selTimer); - break; - case 10: - EmSendCmd(rSAK1, sizeof(rSAK1)); - cardSTATE = MFEMUL_SELECT3; - break; - default:break; - } - break; - } - - // i guess there is a command). go into the work state. - if (len != 4) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - cardSTATE = MFEMUL_WORK; - //goto lbWORK; - //intentional fall-through to the next case-stmt - } - - case MFEMUL_WORK:{ - if (len == 0) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - - bool encrypted_data = (cardAUTHKEY != 0xFF) ; - - if(encrypted_data) { - // decrypt seqence - mf_crypto1_decrypt(pcs, receivedCmd, len); - } - - if (len == 4 && (receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61)) { - - // if authenticating to a block that shouldn't exist - as long as we are not doing the reader attack - if (receivedCmd[1] >= 16 * 4 && !(flags & FLAG_NR_AR_ATTACK)) { - //is this the correct response to an auth on a out of range block? marshmellow - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking",receivedCmd[0],receivedCmd[1],receivedCmd[1]); - break; - } - - authTimer = GetTickCount(); - cardAUTHSC = receivedCmd[1] / 4; // received block num - cardAUTHKEY = receivedCmd[0] - 0x60; - crypto1_destroy(pcs);//Added by martin - crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); - //uint64_t key=emlGetKey(cardAUTHSC, cardAUTHKEY); - //Dbprintf("key: %04x%08x",(uint32_t)(key>>32)&0xFFFF,(uint32_t)(key&0xFFFFFFFF)); - - if (!encrypted_data) { // first authentication - if (MF_DBGLEVEL >= 4) Dbprintf("Reader authenticating for block %d (0x%02x) with key %d",receivedCmd[1] ,receivedCmd[1],cardAUTHKEY ); - - crypto1_word(pcs, cuid ^ nonce, 0);//Update crypto state - num_to_bytes(nonce, 4, rAUTH_AT); // Send nonce - } else { // nested authentication - if (MF_DBGLEVEL >= 4) Dbprintf("Reader doing nested authentication for block %d (0x%02x) with key %d",receivedCmd[1] ,receivedCmd[1],cardAUTHKEY ); - ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0); - num_to_bytes(ans, 4, rAUTH_AT); - } - - EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); - //Dbprintf("Sending rAUTH %02x%02x%02x%02x", rAUTH_AT[0],rAUTH_AT[1],rAUTH_AT[2],rAUTH_AT[3]); - cardSTATE = MFEMUL_AUTH1; - break; - } - - // rule 13 of 7.5.3. in ISO 14443-4. chaining shall be continued - // BUT... ACK --> NACK - if (len == 1 && receivedCmd[0] == CARD_ACK) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } - - // rule 12 of 7.5.3. in ISO 14443-4. R(NAK) --> R(ACK) - if (len == 1 && receivedCmd[0] == CARD_NACK_NA) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - break; - } - - if(len != 4) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - - if(receivedCmd[0] == 0x30 // read block - || receivedCmd[0] == 0xA0 // write block - || receivedCmd[0] == 0xC0 // inc - || receivedCmd[0] == 0xC1 // dec - || receivedCmd[0] == 0xC2 // restore - || receivedCmd[0] == 0xB0) { // transfer - if (receivedCmd[1] >= 16 * 4) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking",receivedCmd[0],receivedCmd[1],receivedCmd[1]); - break; - } - - if (receivedCmd[1] / 4 != cardAUTHSC) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on block (0x%02x) not authenticated for (0x%02x), nacking",receivedCmd[0],receivedCmd[1],cardAUTHSC); - break; - } - } - // read block - if (receivedCmd[0] == 0x30) { - if (MF_DBGLEVEL >= 4) { - Dbprintf("Reader reading block %d (0x%02x)",receivedCmd[1],receivedCmd[1]); - } - emlGetMem(response, receivedCmd[1], 1); - AppendCrc14443a(response, 16); - mf_crypto1_encrypt(pcs, response, 18, response_par); - EmSendCmdPar(response, 18, response_par); - numReads++; - if(exitAfterNReads > 0 && numReads == exitAfterNReads) { - Dbprintf("%d reads done, exiting", numReads); - finished = true; - } - break; - } - // write block - if (receivedCmd[0] == 0xA0) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0xA0 write block %d (%02x)",receivedCmd[1],receivedCmd[1]); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - cardSTATE = MFEMUL_WRITEBL2; - cardWRBL = receivedCmd[1]; - break; - } - // increment, decrement, restore - if (receivedCmd[0] == 0xC0 || receivedCmd[0] == 0xC1 || receivedCmd[0] == 0xC2) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x)",receivedCmd[0],receivedCmd[1],receivedCmd[1]); - if (emlCheckValBl(receivedCmd[1])) { - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate on block, but emlCheckValBl failed, nacking"); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - if (receivedCmd[0] == 0xC1) - cardSTATE = MFEMUL_INTREG_INC; - if (receivedCmd[0] == 0xC0) - cardSTATE = MFEMUL_INTREG_DEC; - if (receivedCmd[0] == 0xC2) - cardSTATE = MFEMUL_INTREG_REST; - cardWRBL = receivedCmd[1]; - break; - } - // transfer - if (receivedCmd[0] == 0xB0) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x transfer block %d (%02x)",receivedCmd[0],receivedCmd[1],receivedCmd[1]); - if (emlSetValBl(cardINTREG, cardINTBLOCK, receivedCmd[1])) - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - else - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - break; - } - // halt - if (receivedCmd[0] == 0x50 && receivedCmd[1] == 0x00) { - LED_B_OFF(); - LED_C_OFF(); - cardSTATE = MFEMUL_HALTED; - if (MF_DBGLEVEL >= 4) Dbprintf("--> HALTED. Selected time: %d ms", GetTickCount() - selTimer); - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - break; - } - // RATS - if (receivedCmd[0] == 0xe0) {//RATS - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } - // command not allowed - if (MF_DBGLEVEL >= 4) Dbprintf("Received command not allowed, nacking"); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } - case MFEMUL_WRITEBL2:{ - if (len == 18){ - mf_crypto1_decrypt(pcs, receivedCmd, len); - emlSetMem(receivedCmd, cardWRBL, 1); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - cardSTATE = MFEMUL_WORK; - } else { - 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); - } - break; - } - - case MFEMUL_INTREG_INC:{ - mf_crypto1_decrypt(pcs, receivedCmd, len); - memcpy(&ans, receivedCmd, 4); - if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - cardSTATE_TO_IDLE(); - break; - } - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - cardINTREG = cardINTREG + ans; - cardSTATE = MFEMUL_WORK; - break; - } - case MFEMUL_INTREG_DEC:{ - mf_crypto1_decrypt(pcs, receivedCmd, len); - memcpy(&ans, receivedCmd, 4); - if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - cardSTATE_TO_IDLE(); - break; - } - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - cardINTREG = cardINTREG - ans; - cardSTATE = MFEMUL_WORK; - break; - } - case MFEMUL_INTREG_REST:{ - mf_crypto1_decrypt(pcs, receivedCmd, len); - memcpy(&ans, receivedCmd, 4); - if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - cardSTATE_TO_IDLE(); - break; - } - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - cardSTATE = MFEMUL_WORK; - break; - } - } - button_pushed = BUTTON_PRESS(); - } - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - - if(flags & FLAG_NR_AR_ATTACK && MF_DBGLEVEL >= 1) { - 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 %s from reader for sector %d:", (i= 1) Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", tracing, BigBuf_get_traceLen()); - - if(flags & FLAG_INTERACTIVE) { // Interactive mode flag, means we need to send ACK - //Send the collected ar_nr in the response - cmd_send(CMD_ACK,CMD_SIMULATE_MIFARE_CARD,button_pushed,0,&ar_nr_resp,sizeof(ar_nr_resp)); - } -} - //----------------------------------------------------------------------------- // MIFARE sniffer. @@ -3098,7 +2480,7 @@ void RAMFUNC SniffMifare(uint8_t param) { for(uint32_t sniffCounter = 0; true; ) { if(BUTTON_PRESS()) { - DbpString("cancelled by button"); + DbpString("Canceled by button."); break; } @@ -3155,7 +2537,9 @@ void RAMFUNC SniffMifare(uint8_t param) { if(!TagIsActive) { // no need to try decoding tag data if the reader is sending uint8_t readerdata = (previous_data & 0xF0) | (*data >> 4); if(MillerDecoding(readerdata, (sniffCounter-1)*4)) { - LED_C_INV(); + LED_B_ON(); + LED_C_OFF(); + if (MfSniffLogic(receivedCmd, Uart.len, Uart.parity, Uart.bitCount, true)) break; /* And ready to receive another command. */ @@ -3170,7 +2554,8 @@ void RAMFUNC SniffMifare(uint8_t param) { if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending uint8_t tagdata = (previous_data << 4) | (*data & 0x0F); if(ManchesterDecoding(tagdata, 0, (sniffCounter-1)*4)) { - LED_C_INV(); + LED_B_OFF(); + LED_C_ON(); if (MfSniffLogic(receivedResponse, Demod.len, Demod.parity, Demod.bitCount, false)) break; @@ -3192,7 +2577,7 @@ void RAMFUNC SniffMifare(uint8_t param) { } // main cycle - DbpString("COMMAND FINISHED"); + DbpString("COMMAND FINISHED."); FpgaDisableSscDma(); MfSniffEnd(); diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 658216e7..8796edf5 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -15,18 +15,42 @@ #include #include +#include "usb_cmd.h" #include "mifare.h" +typedef struct { + uint8_t* response; + uint8_t* modulation; + uint16_t response_n; + uint16_t modulation_n; + uint32_t ProxToAirDuration; + uint8_t par; // enough for precalculated parity of 8 Byte responses +} tag_response_info_t; + extern void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *par); extern void AppendCrc14443a(uint8_t *data, int len); +extern void RAMFUNC SnoopIso14443a(uint8_t param); +extern void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t *data); +extern void ReaderIso14443a(UsbCommand *c); extern void ReaderTransmit(uint8_t *frame, uint16_t len, uint32_t *timing); extern void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing); extern void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing); extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par); +extern void ReaderMifare(bool first_try); + +extern int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity); +extern int EmSendCmd(uint8_t *resp, uint16_t respLen); +extern int EmSendCmdEx(uint8_t *resp, uint16_t respLen); +extern int EmSend4bit(uint8_t resp); +extern int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par); +extern int EmSendPrecompiledCmd(tag_response_info_t *response_info); + +extern bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *buffer_size); extern void iso14443a_setup(uint8_t fpga_minor_mode); extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data); -extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades); +extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); extern void iso14a_set_trigger(bool enable); +extern void iso14a_set_timeout(uint32_t timeout); #endif /* __ISO14443A_H */ diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 22227e74..cb8567fa 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Jonathan Westhues, split Nov 2006 +// piwi 2018 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -16,8 +17,8 @@ #include "iso14443crc.h" -#define RECEIVE_SAMPLES_TIMEOUT 2000 -#define ISO14443B_DMA_BUFFER_SIZE 256 +#define RECEIVE_SAMPLES_TIMEOUT 1000 // TR0 max is 256/fs = 256/(848kHz) = 302us or 64 samples from FPGA. 1000 seems to be much too high? +#define ISO14443B_DMA_BUFFER_SIZE 128 // PCB Block number for APDUs static uint8_t pcb_blocknum = 0; @@ -243,7 +244,7 @@ static RAMFUNC int Handle14443bUartBit(uint8_t bit) LED_A_OFF(); // Finished receiving Uart.state = STATE_UNSYNCD; if (Uart.byteCnt != 0) { - return TRUE; + return true; } } else { // this is an error @@ -259,7 +260,7 @@ static RAMFUNC int Handle14443bUartBit(uint8_t bit) break; } - return FALSE; + return false; } @@ -283,7 +284,7 @@ static void UartInit(uint8_t *data) // Receive a command (from the reader to us, where we are the simulated tag), // and store it in the given buffer, up to the given maximum length. Keeps // spinning, waiting for a well-framed command, until either we get one -// (returns TRUE) or someone presses the pushbutton on the board (FALSE). +// (returns true) or someone presses the pushbutton on the board (false). // // Assume that we're called with the SSC (to the FPGA) and ADC path set // correctly. @@ -302,20 +303,20 @@ static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) for(;;) { WDT_HIT(); - if(BUTTON_PRESS()) return FALSE; + if(BUTTON_PRESS()) return false; if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; for(uint8_t mask = 0x80; mask != 0x00; mask >>= 1) { if(Handle14443bUartBit(b & mask)) { *len = Uart.byteCnt; - return TRUE; + return true; } } } } - return FALSE; + return false; } //----------------------------------------------------------------------------- @@ -347,7 +348,7 @@ void SimulateIso14443bTag(void) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); clear_trace(); - set_tracing(TRUE); + set_tracing(true); const uint8_t *resp; uint8_t *respCode; @@ -374,7 +375,7 @@ void SimulateIso14443bTag(void) // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); cmdsRecvd = 0; @@ -387,7 +388,7 @@ void SimulateIso14443bTag(void) if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(receivedCmd, len, 0, 0, parity, TRUE); + LogTrace(receivedCmd, len, 0, 0, parity, true); } // Good, look at the command now. @@ -440,7 +441,7 @@ void SimulateIso14443bTag(void) LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK); AT91C_BASE_SSC->SSC_THR = 0xff; - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // Transmit the response. uint16_t i = 0; @@ -464,7 +465,7 @@ void SimulateIso14443bTag(void) // trace the response: if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(resp, respLen, 0, 0, parity, FALSE); + LogTrace(resp, respLen, 0, 0, parity, false); } } @@ -535,60 +536,11 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) #define SUBCARRIER_DETECT_THRESHOLD 8 -// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by abs(ci) + abs(cq) -/* #define CHECK_FOR_SUBCARRIER() { \ - v = ci; \ - if(v < 0) v = -v; \ - if(cq > 0) { \ - v += cq; \ - } else { \ - v -= cq; \ - } \ - } - */ // Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) - - //note: couldn't we just use MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2) from common.h - marshmellow -#define CHECK_FOR_SUBCARRIER() { \ - v = MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2); \ - } - /* - if(ci < 0) { \ - if(cq < 0) { \ // ci < 0, cq < 0 - if (cq < ci) { \ - v = -cq - (ci >> 1); \ - } else { \ - v = -ci - (cq >> 1); \ - } \ - } else { \ // ci < 0, cq >= 0 - if (cq < -ci) { \ - v = -ci + (cq >> 1); \ - } else { \ - v = cq - (ci >> 1); \ - } \ - } \ - } else { \ - if(cq < 0) { \ // ci >= 0, cq < 0 - if (-cq < ci) { \ - v = ci - (cq >> 1); \ - } else { \ - v = -cq + (ci >> 1); \ - } \ - } else { \ // ci >= 0, cq >= 0 - if (cq < ci) { \ - v = ci + (cq >> 1); \ - } else { \ - v = cq + (ci >> 1); \ - } \ - } \ - } \ - } - */ - +#define AMPLITUDE(ci,cq) (MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2)) switch(Demod.state) { case DEMOD_UNSYNCD: - CHECK_FOR_SUBCARRIER(); - if(v > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected + if(AMPLITUDE(ci,cq) > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected Demod.state = DEMOD_PHASE_REF_TRAINING; Demod.sumI = ci; Demod.sumQ = cq; @@ -598,8 +550,7 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) case DEMOD_PHASE_REF_TRAINING: if(Demod.posCount < 8) { - CHECK_FOR_SUBCARRIER(); - if (v > SUBCARRIER_DETECT_THRESHOLD) { + if (AMPLITUDE(ci,cq) > SUBCARRIER_DETECT_THRESHOLD) { // set the reference phase (will code a logic '1') by averaging over 32 1/fs. // note: synchronization time > 80 1/fs Demod.sumI += ci; @@ -702,7 +653,7 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) LED_C_OFF(); if(s == 0x000) { // This is EOF (start, stop and all data bits == '0' - return TRUE; + return true; } } } @@ -716,7 +667,7 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) break; } - return FALSE; + return false; } @@ -739,14 +690,15 @@ static void DemodInit(uint8_t *data) /* * Demodulate the samples we received from the tag, also log to tracebuffer - * quiet: set to 'TRUE' to disable debug output + * quiet: set to 'true' to disable debug output */ static void GetSamplesFor14443bDemod(int n, bool quiet) { - int max = 0; - bool gotFrame = FALSE; - int lastRxCounter, ci, cq, samples = 0; - + int maxBehindBy = 0; + bool gotFrame = false; + int lastRxCounter, samples = 0; + int8_t ci, cq; + // Allocate memory from BigBuf for some buffers // free all previous allocations first BigBuf_free(); @@ -755,15 +707,19 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); // Set up the demodulator for tag -> reader responses. DemodInit(receivedResponse); + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) + // Setup and start DMA. + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); - int8_t *upTo = dmaBuf; + uint16_t *upTo = dmaBuf; lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; // Signal field is ON with the appropriate LED: @@ -772,43 +728,44 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); for(;;) { - int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; - if(behindBy > max) max = behindBy; - - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1)) > 2) { - ci = upTo[0]; - cq = upTo[1]; - upTo += 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - upTo = dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; - } - lastRxCounter -= 2; - if(lastRxCounter <= 0) { - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - } - - samples += 2; - - if(Handle14443bSamplesDemod(ci, cq)) { - gotFrame = TRUE; - break; - } + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; } - if(samples > n || gotFrame) { + if(behindBy < 1) continue; + + ci = *upTo >> 8; + cq = *upTo; + upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + samples++; + + if(Handle14443bSamplesDemod(ci, cq)) { + gotFrame = true; + break; + } + + if(samples > n) { break; } } - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; + FpgaDisableSscDma(); - if (!quiet) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", max, samples, gotFrame, Demod.len, Demod.sumI, Demod.sumQ); + if (!quiet) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", maxBehindBy, samples, gotFrame, Demod.len, Demod.sumI, Demod.sumQ); //Tracing if (tracing && Demod.len > 0) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(Demod.output, Demod.len, 0, 0, parity, FALSE); + LogTrace(Demod.output, Demod.len, 0, 0, parity, false); } } @@ -820,11 +777,7 @@ static void TransmitFor14443b(void) { int c; - FpgaSetupSsc(); - - while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - } + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); // Signal field is ON with the appropriate Red LED LED_D_ON(); @@ -832,31 +785,15 @@ static void TransmitFor14443b(void) LED_B_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - for(c = 0; c < 10;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - c++; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); - } - c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; + AT91C_BASE_SSC->SSC_THR = ~ToSend[c]; c++; if(c >= ToSendMax) { break; } } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } WDT_HIT(); } LED_B_OFF(); // Finished sending @@ -874,19 +811,14 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) ToSendReset(); - // Establish initial reference level - for(i = 0; i < 40; i++) { - ToSendStuffBit(1); - } // Send SOF for(i = 0; i < 10; i++) { ToSendStuffBit(0); } + ToSendStuffBit(1); + ToSendStuffBit(1); for(i = 0; i < len; i++) { - // Stop bits/EGT - ToSendStuffBit(1); - ToSendStuffBit(1); // Start bit ToSendStuffBit(0); // Data bits @@ -899,19 +831,18 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) } b >>= 1; } - } - // Send EOF - ToSendStuffBit(1); - for(i = 0; i < 10; i++) { - ToSendStuffBit(0); - } - for(i = 0; i < 8; i++) { + // Stop bit ToSendStuffBit(1); } - // And then a little more, to make sure that the last character makes - // it out before we switch to rx mode. - for(i = 0; i < 24; i++) { + // Send EOF + for(i = 0; i < 10; i++) { + ToSendStuffBit(0); + } + ToSendStuffBit(1); + + // ensure that last byte is filled up + for(i = 0; i < 8; i++) { ToSendStuffBit(1); } @@ -929,7 +860,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) TransmitFor14443b(); if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(cmd,len, 0, 0, parity, TRUE); + LogTrace(cmd,len, 0, 0, parity, true); } } @@ -951,7 +882,7 @@ int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *respo // send CodeAndTransmit14443bAsReader(message_frame, message_length + 4); // get response - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT*100, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if(Demod.len < 3) { return 0; @@ -981,7 +912,7 @@ int iso14443b_select_card() // first, wake up the tag CodeAndTransmit14443bAsReader(wupb, sizeof(wupb)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); // ATQB too short? if (Demod.len < 14) { @@ -996,7 +927,7 @@ int iso14443b_select_card() attrib[7] = Demod.output[10] & 0x0F; ComputeCrc14443(CRC_14443_B, attrib, 9, attrib + 9, attrib + 10); CodeAndTransmit14443bAsReader(attrib, sizeof(attrib)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); // Answer to ATTRIB too short? if(Demod.len < 3) { @@ -1011,7 +942,7 @@ int iso14443b_select_card() void iso14443b_setup() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1019,9 +950,6 @@ void iso14443b_setup() { LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - // Start the timer - StartCountSspClk(); - DemodReset(); UartReset(); } @@ -1047,7 +975,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) SpinDelay(200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Now give it time to spin up. // Signal field is on with the appropriate LED @@ -1056,15 +984,17 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) SpinDelay(200); clear_trace(); - set_tracing(TRUE); + set_tracing(true); // First command: wake up the tag using the INITIATE command uint8_t cmd1[] = {0x06, 0x00, 0x97, 0x5b}; CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len == 0) { DbpString("No response from tag"); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } else { Dbprintf("Randomly generated Chip ID (+ 2 byte CRC): %02x %02x %02x", @@ -1077,20 +1007,26 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) cmd1[1] = Demod.output[0]; ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 3) { Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // Check the CRC of the answer: ComputeCrc14443(CRC_14443_B, Demod.output, 1 , &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[1] || cmd1[3] != Demod.output[2]) { DbpString("CRC Error reading select response."); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // Check response from the tag: should be the same UID as the command we just sent: if (cmd1[1] != Demod.output[0]) { Dbprintf("Bad response to SELECT from Tag, aborting: %02x %02x", cmd1[1], Demod.output[0]); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } @@ -1099,9 +1035,11 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) cmd1[0] = 0x0B; ComputeCrc14443(CRC_14443_B, cmd1, 1 , &cmd1[1], &cmd1[2]); CodeAndTransmit14443bAsReader(cmd1, 3); // Only first three bytes for this one - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 10) { Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // The check the CRC of the answer (use cmd1 as temporary variable): @@ -1128,9 +1066,11 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) cmd1[1] = i; ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 6) { // Check if we got an answer from the tag DbpString("Expected 6 bytes from tag, got less..."); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // The check the CRC of the answer (use cmd1 as temporary variable): @@ -1149,6 +1089,9 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) } i++; } + + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); } @@ -1171,22 +1114,17 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) */ void RAMFUNC SnoopIso14443b(void) { - // We won't start recording the frames that we acquire until we trigger; - // a good trigger condition to get started is probably when we see a - // response from the tag. - int triggered = TRUE; // TODO: set and evaluate trigger condition - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BigBuf_free(); clear_trace(); - set_tracing(TRUE); + set_tracing(true); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); int lastRxCounter; - int8_t *upTo; - int ci, cq; + uint16_t *upTo; + int8_t ci, cq; int maxBehindBy = 0; // Count of samples received so far, so that we can include timing @@ -1211,55 +1149,57 @@ void RAMFUNC SnoopIso14443b(void) SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); upTo = dmaBuf; lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); uint8_t parity[MAX_PARITY_SIZE]; - bool TagIsActive = FALSE; - bool ReaderIsActive = FALSE; + bool TagIsActive = false; + bool ReaderIsActive = false; + // We won't start recording the frames that we acquire until we trigger. + // A good trigger condition to get started is probably when we see a + // reader command + bool triggered = false; // And now we loop, receiving samples. for(;;) { - int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (ISO14443B_DMA_BUFFER_SIZE-1); + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); if(behindBy > maxBehindBy) { maxBehindBy = behindBy; } - if(behindBy < 2) continue; + if(behindBy < 1) continue; - ci = upTo[0]; - cq = upTo[1]; - upTo += 2; - lastRxCounter -= 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - upTo = dmaBuf; + ci = *upTo>>8; + cq = *upTo; + upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning again lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + break; + } + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers WDT_HIT(); - if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? - Dbprintf("blew circular buffer! behindBy=%d", behindBy); - break; - } - if(!tracing) { - DbpString("Reached trace limit"); - break; - } if(BUTTON_PRESS()) { DbpString("cancelled"); break; } } - samples += 2; + samples++; if (!TagIsActive) { // no need to try decoding reader data if the tag is sending if(Handle14443bUartBit(ci & 0x01)) { - if(triggered && tracing) { - LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); + triggered = true; + if(tracing) { + LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, true); } /* And ready to receive another command. */ UartReset(); @@ -1268,8 +1208,9 @@ void RAMFUNC SnoopIso14443b(void) DemodReset(); } if(Handle14443bUartBit(cq & 0x01)) { - if(triggered && tracing) { - LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); + triggered = true; + if(tracing) { + LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, true); } /* And ready to receive another command. */ UartReset(); @@ -1280,17 +1221,15 @@ void RAMFUNC SnoopIso14443b(void) ReaderIsActive = (Uart.state > STATE_GOT_FALLING_EDGE_OF_SOF); } - if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time - if(Handle14443bSamplesDemod(ci | 0x01, cq | 0x01)) { + if(!ReaderIsActive && triggered) { // no need to try decoding tag data if the reader is sending or not yet triggered + if(Handle14443bSamplesDemod(ci/2, cq/2)) { //Use samples as a time measurement if(tracing) { uint8_t parity[MAX_PARITY_SIZE]; - LogTrace(Demod.output, Demod.len, samples, samples, parity, FALSE); + LogTrace(Demod.output, Demod.len, samples, samples, parity, false); } - triggered = TRUE; - // And ready to receive another response. DemodReset(); } @@ -1301,7 +1240,6 @@ void RAMFUNC SnoopIso14443b(void) FpgaDisableSscDma(); LEDsoff(); - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; DbpString("Snoop statistics:"); Dbprintf(" Max behind by: %i", maxBehindBy); Dbprintf(" Uart State: %x", Uart.state); @@ -1327,15 +1265,19 @@ void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, u { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + + // switch field on and give tag some time to power up + LED_D_ON(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); + SpinDelay(10); if (datalen){ - set_tracing(TRUE); + set_tracing(true); CodeAndTransmit14443bAsReader(data, datalen); if(recv) { - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); uint16_t iLen = MIN(Demod.len, USB_CMD_DATA_SIZE); cmd_send(CMD_ACK, iLen, 0, 0, Demod.output, iLen); } diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index f90c54f3..de6faa92 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -7,7 +7,7 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Routines to support ISO 14443 type A. +// Routines to support ISO 14443 type B. //----------------------------------------------------------------------------- #ifndef __ISO14443B_H diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index e11e5a9c..da2aab69 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -2,33 +2,31 @@ // Jonathan Westhues, split Nov 2006 // Modified by Greg Jones, Jan 2009 // Modified by Adrian Dabrowski "atrox", Mar-Sept 2010,Oct 2011 +// Modified by piwi, Oct 2018 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- // Routines to support ISO 15693. This includes both the reader software and -// the `fake tag' modes, but at the moment I've implemented only the reader -// stuff, and that barely. -// Modified to perform modulation onboard in arm rather than on PC -// Also added additional reader commands (SELECT, READ etc.) +// the `fake tag' modes. //----------------------------------------------------------------------------- -// The ISO 15693 describes two transmission modes from reader to tag, and 4 -// transmission modes from tag to reader. As of Mar 2010 this code only -// supports one of each: "1of4" mode from reader to tag, and the highspeed -// variant with one subcarrier from card to reader. -// As long, as the card fully support ISO 15693 this is no problem, since the -// reader chooses both data rates, but some non-standard tags do not. Further for -// the simulation to work, we will need to support all data rates. + +// The ISO 15693 describes two transmission modes from reader to tag, and four +// transmission modes from tag to reader. As of Oct 2018 this code supports +// both reader modes and the high speed variant with one subcarrier from card to reader. +// As long as the card fully support ISO 15693 this is no problem, since the +// reader chooses both data rates, but some non-standard tags do not. +// For card simulation, the code supports both high and low speed modes with one subcarrier. // // VCD (reader) -> VICC (tag) // 1 out of 256: -// data rate: 1,66 kbit/s (fc/8192) +// data rate: 1,66 kbit/s (fc/8192) // used for long range // 1 out of 4: // data rate: 26,48 kbit/s (fc/512) // used for short range, high speed -// +// // VICC (tag) -> VCD (reader) // Modulation: // ASK / one subcarrier (423,75 khz) @@ -39,22 +37,17 @@ // high ASK: 26,48 kbit/s // high FSK: 26,69 kbit/s //----------------------------------------------------------------------------- -// added "1 out of 256" mode (for VCD->PICC) - atrox 20100911 // Random Remarks: // *) UID is always used "transmission order" (LSB), which is reverse to display order // TODO / BUGS / ISSUES: -// *) writing to tags takes longer: we miss the answer from the tag in most cases -// -> tweak the read-timeout times -// *) signal decoding from the card is still a bit shaky. -// *) signal decoding is unable to detect collissions. -// *) add anti-collission support for inventory-commands +// *) signal decoding is unable to detect collisions. +// *) add anti-collision support for inventory-commands // *) read security status of a block -// *) sniffing and simulation do only support one transmission mode. need to support -// all 8 transmission combinations -// *) remove or refactor code under "depricated" +// *) sniffing and simulation do not support two subcarrier modes. +// *) remove or refactor code under "depricated" // *) document all the functions @@ -63,10 +56,13 @@ #include "apps.h" #include "string.h" #include "iso15693tools.h" +#include "protocols.h" #include "cmd.h" #define arraylen(x) (sizeof(x)/sizeof((x)[0])) +static int DEBUG = 0; + /////////////////////////////////////////////////////////////////////// // ISO 15693 Part 2 - Air Interface // This section basicly contains transmission and receiving of bits @@ -81,15 +77,25 @@ #define AddCrc(data,datalen) Iso15693AddCrc(data,datalen) #define sprintUID(target,uid) Iso15693sprintUID(target,uid) -int DEBUG=0; +// approximate amplitude=sqrt(ci^2+cq^2) by amplitude = max(|ci|,|cq|) + 1/2*min(|ci|,|cq|) +#define AMPLITUDE(ci, cq) (MAX(ABS(ci), ABS(cq)) + MIN(ABS(ci), ABS(cq))/2) +// buffers +#define ISO15693_DMA_BUFFER_SIZE 128 +#define ISO15693_MAX_RESPONSE_LENGTH 36 // allows read single block with the maximum block size of 256bits. Read multiple blocks not supported yet +#define ISO15693_MAX_COMMAND_LENGTH 45 // allows write single block with the maximum block size of 256bits. Write multiple blocks not supported yet + +// timing. Delays in SSP_CLK ticks. +#define DELAY_READER_TO_ARM 8 +#define DELAY_ARM_TO_READER 1 +#define DELAY_ISO15693_VCD_TO_VICC 132 // 132/423.75kHz = 311.5us from end of EOF to start of tag response // --------------------------- -// Signal Processing +// Signal Processing // --------------------------- // prepare data using "1 out of 4" code for later transmission -// resulting data rate is 26,48 kbit/s (fc/512) +// resulting data rate is 26.48 kbit/s (fc/512) // cmd ... data // n ... length of data static void CodeIso15693AsReader(uint8_t *cmd, int n) @@ -165,14 +171,16 @@ static void CodeIso15693AsReader(uint8_t *cmd, int n) ToSendStuffBit(0); ToSendStuffBit(1); - // And slack at the end, too. - for(i = 0; i < 24; i++) { + // Fill remainder of last byte with 1 + for(i = 0; i < 4; i++) { ToSendStuffBit(1); } + + ToSendMax++; } -// encode data using "1 out of 256" sheme -// data rate is 1,66 kbit/s (fc/8192) +// encode data using "1 out of 256" scheme +// data rate is 1,66 kbit/s (fc/8192) // is designed for more robust communication over longer distances static void CodeIso15693AsReader256(uint8_t *cmd, int n) { @@ -194,7 +202,7 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) ToSendStuffBit(1); ToSendStuffBit(1); ToSendStuffBit(0); - + for(i = 0; i < n; i++) { for (j = 0; j<=255; j++) { if (cmd[i]==j) { @@ -203,8 +211,8 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) } else { ToSendStuffBit(1); ToSendStuffBit(1); - } - } + } + } } // EOF ToSendStuffBit(1); @@ -212,357 +220,722 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) ToSendStuffBit(0); ToSendStuffBit(1); - // And slack at the end, too. - for(i = 0; i < 24; i++) { + // Fill remainder of last byte with 1 + for(i = 0; i < 4; i++) { ToSendStuffBit(1); } + + ToSendMax++; } -// Transmit the command (to the tag) that was placed in ToSend[]. -static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *wait) +static void CodeIso15693AsTag(uint8_t *cmd, int n) { - int c; + ToSendReset(); -// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + // SOF + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(1); + + // data + for(int i = 0; i < n; i++) { + for(int j = 0; j < 8; j++) { + if ((cmd[i] >> j) & 0x01) { + ToSendStuffBit(0); + ToSendStuffBit(1); + } else { + ToSendStuffBit(1); + ToSendStuffBit(0); + } + } + } + + // EOF + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(0); + ToSendStuffBit(0); + ToSendStuffBit(0); + + ToSendMax++; +} + + +// Transmit the command (to the tag) that was placed in cmd[]. +static void TransmitTo15693Tag(const uint8_t *cmd, int len) +{ + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - if(*wait < 10) { *wait = 10; } -// for(c = 0; c < *wait;) { -// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { -// AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! -// c++; -// } -// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { -// volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; -// (void)r; -// } -// WDT_HIT(); -// } - - c = 0; - for(;;) { + LED_B_ON(); + for(int c = 0; c < len; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; + AT91C_BASE_SSC->SSC_THR = ~cmd[c]; c++; - if(c >= len) { - break; - } - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; } WDT_HIT(); } - *samples = (c + *wait) << 3; + LED_B_OFF(); } //----------------------------------------------------------------------------- -// Transmit the command (to the reader) that was placed in ToSend[]. +// Transmit the tag response (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- -static void TransmitTo15693Reader(const uint8_t *cmd, int len, int *samples, int *wait) +static void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t start_time, bool slow) { - int c = 0; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR|FPGA_HF_SIMULATOR_MODULATE_424K); - if(*wait < 10) { *wait = 10; } + // don't use the FPGA_HF_SIMULATOR_MODULATE_424K_8BIT minor mode. It would spoil GetCountSspClk() + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; - c++; - if(c >= len) { - break; - } + uint8_t shift_delay = start_time & 0x00000007; + uint8_t bitmask = 0x00; + for (int i = 0; i < shift_delay; i++) { + bitmask |= (0x01 << i); + } + + while (GetCountSspClk() < (start_time & 0xfffffff8)) ; + AT91C_BASE_SSC->SSC_THR = 0x00; // clear TXRDY + + LED_C_ON(); + uint8_t bits_to_shift = 0x00; + for(size_t c = 0; c <= len; c++) { + uint8_t bits_to_send = bits_to_shift << (8 - shift_delay) | (c==len?0x00:cmd[c]) >> shift_delay; + bits_to_shift = cmd[c] & bitmask; + for (int i = 7; i >= 0; i--) { + for (int j = 0; j < (slow?4:1); ) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + if (bits_to_send >> i & 0x01) { + AT91C_BASE_SSC->SSC_THR = 0xff; + } else { + AT91C_BASE_SSC->SSC_THR = 0x00; + } + j++; + } + WDT_HIT(); + } } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); } - *samples = (c + *wait) << 3; + LED_C_OFF(); } -// Read from Tag -// Parameters: -// receivedResponse -// maxLen -// samples -// elapsed -// returns: -// number of decoded bytes -static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) +//============================================================================= +// An ISO 15693 decoder for tag responses (one subcarrier only). +// Uses cross correlation to identify the SOF, each bit, and EOF. +// This function is called 8 times per bit (every 2 subcarrier cycles). +// Subcarrier frequency fs is 424kHz, 1/fs = 2,36us, +// i.e. function is called every 4,72us +// LED handling: +// LED C -> ON once we have received the SOF and are expecting the rest. +// LED C -> OFF once we have received EOF or are unsynced +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= + +#define SUBCARRIER_DETECT_THRESHOLD 2 +#define SOF_CORRELATOR_LEN (1<<5) + +typedef struct DecodeTag { + enum { + STATE_TAG_UNSYNCD, + STATE_TAG_AWAIT_SOF_1, + STATE_TAG_AWAIT_SOF_2, + STATE_TAG_RECEIVING_DATA, + STATE_TAG_AWAIT_EOF + } state; + int bitCount; + int posCount; + enum { + LOGIC0, + LOGIC1, + SOF_PART1, + SOF_PART2 + } lastBit; + uint16_t shiftReg; + uint8_t *output; + int len; + int sum1, sum2; + uint8_t SOF_low; + uint8_t SOF_high; + uint8_t SOF_last; + int32_t SOF_corr; + int32_t SOF_corr_prev; + uint8_t SOF_correlator[SOF_CORRELATOR_LEN]; +} DecodeTag_t; + +static int Handle15693SamplesFromTag(int8_t ci, int8_t cq, DecodeTag_t *DecodeTag) { - int c = 0; - uint8_t *dest = BigBuf_get_addr(); - int getNext = 0; + switch(DecodeTag->state) { + case STATE_TAG_UNSYNCD: + // initialize SOF correlator. We are looking for 12 samples low and 12 samples high. + DecodeTag->SOF_low = 0; + DecodeTag->SOF_high = 12; + DecodeTag->SOF_last = 23; + memset(DecodeTag->SOF_correlator, 0x00, DecodeTag->SOF_last + 1); + DecodeTag->SOF_correlator[DecodeTag->SOF_last] = AMPLITUDE(ci,cq); + DecodeTag->SOF_corr = DecodeTag->SOF_correlator[DecodeTag->SOF_last]; + DecodeTag->SOF_corr_prev = DecodeTag->SOF_corr; + // initialize Decoder + DecodeTag->posCount = 0; + DecodeTag->bitCount = 0; + DecodeTag->len = 0; + DecodeTag->state = STATE_TAG_AWAIT_SOF_1; + break; - int8_t prev = 0; + case STATE_TAG_AWAIT_SOF_1: + // calculate the correlation in real time. Look at differences only. + DecodeTag->SOF_corr += DecodeTag->SOF_correlator[DecodeTag->SOF_low++]; + DecodeTag->SOF_corr -= 2*DecodeTag->SOF_correlator[DecodeTag->SOF_high++]; + DecodeTag->SOF_last++; + DecodeTag->SOF_low &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_high &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_last &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_correlator[DecodeTag->SOF_last] = AMPLITUDE(ci,cq); + DecodeTag->SOF_corr += DecodeTag->SOF_correlator[DecodeTag->SOF_last]; -// NOW READ RESPONSE - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads - c = 0; - getNext = FALSE; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; + // if correlation increases for 10 consecutive samples, we are close to maximum correlation + if (DecodeTag->SOF_corr > DecodeTag->SOF_corr_prev + SUBCARRIER_DETECT_THRESHOLD) { + DecodeTag->posCount++; + } else { + DecodeTag->posCount = 0; + } - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + if (DecodeTag->posCount == 10) { // correlation increased 10 times + DecodeTag->state = STATE_TAG_AWAIT_SOF_2; + } - dest[c++] = (uint8_t)r; + DecodeTag->SOF_corr_prev = DecodeTag->SOF_corr; - if(c >= 2000) { - break; + break; + + case STATE_TAG_AWAIT_SOF_2: + // calculate the correlation in real time. Look at differences only. + DecodeTag->SOF_corr += DecodeTag->SOF_correlator[DecodeTag->SOF_low++]; + DecodeTag->SOF_corr -= 2*DecodeTag->SOF_correlator[DecodeTag->SOF_high++]; + DecodeTag->SOF_last++; + DecodeTag->SOF_low &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_high &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_last &= (SOF_CORRELATOR_LEN-1); + DecodeTag->SOF_correlator[DecodeTag->SOF_last] = AMPLITUDE(ci,cq); + DecodeTag->SOF_corr += DecodeTag->SOF_correlator[DecodeTag->SOF_last]; + + if (DecodeTag->SOF_corr >= DecodeTag->SOF_corr_prev) { // we are looking for the maximum correlation + DecodeTag->SOF_corr_prev = DecodeTag->SOF_corr; + } else { + DecodeTag->lastBit = SOF_PART1; // detected 1st part of SOF + DecodeTag->sum1 = DecodeTag->SOF_correlator[DecodeTag->SOF_last]; + DecodeTag->sum2 = 0; + DecodeTag->posCount = 2; + DecodeTag->state = STATE_TAG_RECEIVING_DATA; + LED_C_ON(); + } + + break; + + case STATE_TAG_RECEIVING_DATA: + if (DecodeTag->posCount == 1) { + DecodeTag->sum1 = 0; + DecodeTag->sum2 = 0; + } + + if (DecodeTag->posCount <= 4) { + DecodeTag->sum1 += AMPLITUDE(ci, cq); + } else { + DecodeTag->sum2 += AMPLITUDE(ci, cq); + } + + if (DecodeTag->posCount == 8) { + int16_t corr_1 = (DecodeTag->sum2 - DecodeTag->sum1) / 4; + int16_t corr_0 = (DecodeTag->sum1 - DecodeTag->sum2) / 4; + int16_t corr_EOF = (DecodeTag->sum1 + DecodeTag->sum2) / 8; + if (corr_EOF > corr_0 && corr_EOF > corr_1) { + DecodeTag->state = STATE_TAG_AWAIT_EOF; + } else if (corr_1 > corr_0) { + // logic 1 + if (DecodeTag->lastBit == SOF_PART1) { // still part of SOF + DecodeTag->lastBit = SOF_PART2; + } else { + DecodeTag->lastBit = LOGIC1; + DecodeTag->shiftReg >>= 1; + DecodeTag->shiftReg |= 0x80; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; + DecodeTag->len++; + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + } else { + // logic 0 + if (DecodeTag->lastBit == SOF_PART1) { // incomplete SOF + DecodeTag->state = STATE_TAG_UNSYNCD; + LED_C_OFF(); + } else { + DecodeTag->lastBit = LOGIC0; + DecodeTag->shiftReg >>= 1; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len] = DecodeTag->shiftReg; + DecodeTag->len++; + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } } - } else { - prev = b; + DecodeTag->posCount = 0; } + DecodeTag->posCount++; + break; - getNext = !getNext; - } + case STATE_TAG_AWAIT_EOF: + if (DecodeTag->lastBit == LOGIC0) { // this was already part of EOF + LED_C_OFF(); + return true; + } else { + DecodeTag->state = STATE_TAG_UNSYNCD; + LED_C_OFF(); + } + break; + + default: + DecodeTag->state = STATE_TAG_UNSYNCD; + LED_C_OFF(); + break; } - ////////////////////////////////////////// - /////////// DEMODULATE /////////////////// - ////////////////////////////////////////// - - int i, j; - int max = 0, maxPos=0; - - int skip = 4; - - // if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL - - // First, correlate for SOF - for(i = 0; i < 100; i++) { - int corr = 0; - for(j = 0; j < arraylen(FrameSOF); j += skip) { - corr += FrameSOF[j]*dest[i+(j/skip)]; - } - if(corr > max) { - max = corr; - maxPos = i; - } - } - // Dbprintf("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); - - int k = 0; // this will be our return value - - // greg - If correlation is less than 1 then there's little point in continuing - if ((max/(arraylen(FrameSOF)/skip)) >= 1) - { - - i = maxPos + arraylen(FrameSOF)/skip; - - uint8_t outBuf[20]; - memset(outBuf, 0, sizeof(outBuf)); - uint8_t mask = 0x01; - for(;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; - for(j = 0; j < arraylen(Logic0); j += skip) { - corr0 += Logic0[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(Logic1); j += skip) { - corr1 += Logic1[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(FrameEOF); j += skip) { - corrEOF += FrameEOF[j]*dest[i+(j/skip)]; - } - // Even things out by the length of the target waveform. - corr0 *= 4; - corr1 *= 4; - - if(corrEOF > corr1 && corrEOF > corr0) { - // Dbprintf("EOF at %d", i); - break; - } else if(corr1 > corr0) { - i += arraylen(Logic1)/skip; - outBuf[k] |= mask; - } else { - i += arraylen(Logic0)/skip; - } - mask <<= 1; - if(mask == 0) { - k++; - mask = 0x01; - } - if((i+(int)arraylen(FrameEOF)) >= 2000) { - DbpString("ran off end!"); - break; - } - } - if(mask != 0x01) { // this happens, when we miss the EOF - // TODO: for some reason this happens quite often - if (DEBUG) Dbprintf("error, uneven octet! (extra bits!) mask=%02x", mask); - if (mask<0x08) k--; // discard the last uneven octet; - // 0x08 is an assumption - but works quite often - } - // uint8_t str1 [8]; - // itoa(k,str1); - // strncat(str1," octets read",8); - - // DbpString( str1); // DbpString("%d octets", k); - - // for(i = 0; i < k; i+=3) { - // //DbpString("# %2d: %02x ", i, outBuf[i]); - // DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]); - // } - - for(i = 0; i < k; i++) { - receivedResponse[i] = outBuf[i]; - } - } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip)) - return k; // return the number of bytes demodulated - -/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2)); - + return false; } -// Now the GetISO15693 message from sniffing command -static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) +static void DecodeTagInit(DecodeTag_t *DecodeTag, uint8_t *data) { - int c = 0; - uint8_t *dest = BigBuf_get_addr(); - int getNext = 0; + DecodeTag->output = data; + DecodeTag->state = STATE_TAG_UNSYNCD; +} - int8_t prev = 0; +/* + * Receive and decode the tag response, also log to tracebuffer + */ +static int GetIso15693AnswerFromTag(uint8_t* response, int timeout) +{ + int maxBehindBy = 0; + int lastRxCounter, samples = 0; + int8_t ci, cq; + bool gotFrame = false; -// NOW READ RESPONSE + uint16_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + + // the Decoder data structure + DecodeTag_t DecodeTag; + DecodeTagInit(&DecodeTag, response); + + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + + // And put the FPGA in the appropriate mode FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads - c = 0; - getNext = FALSE; + + // Setup and start DMA. + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); + uint16_t *upTo = dmaBuf; + lastRxCounter = ISO15693_DMA_BUFFER_SIZE; + for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO15693_DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = ABS(b) + ABS(prev); + if (behindBy < 1) continue; - dest[c++] = (uint8_t)r; + ci = (int8_t)(*upTo >> 8); + cq = (int8_t)(*upTo & 0xff); - if(c >= 20000) { - break; + upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + lastRxCounter += ISO15693_DMA_BUFFER_SIZE; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + samples++; + + if (Handle15693SamplesFromTag(ci, cq, &DecodeTag)) { + gotFrame = true; + break; + } + + if(samples > timeout && DecodeTag.state < STATE_TAG_RECEIVING_DATA) { + DecodeTag.len = 0; + break; + } + + } + + FpgaDisableSscDma(); + + if (DEBUG) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", + maxBehindBy, samples, gotFrame, DecodeTag.state, DecodeTag.len, DecodeTag.bitCount, DecodeTag.posCount); + + if (tracing && DecodeTag.len > 0) { + LogTrace(DecodeTag.output, DecodeTag.len, 0, 0, NULL, false); + } + + return DecodeTag.len; +} + + +//============================================================================= +// An ISO15693 decoder for reader commands. +// +// This function is called 4 times per bit (every 2 subcarrier cycles). +// Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 2,36us +// LED handling: +// LED B -> ON once we have received the SOF and are expecting the rest. +// LED B -> OFF once we have received EOF or are in error state or unsynced +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= + +typedef struct DecodeReader { + enum { + STATE_READER_UNSYNCD, + STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_256 + } state; + enum { + CODING_1_OUT_OF_4, + CODING_1_OUT_OF_256 + } Coding; + uint8_t shiftReg; + uint8_t bitCount; + int byteCount; + int byteCountMax; + int posCount; + int sum1, sum2; + uint8_t *output; +} DecodeReader_t; + + +static int Handle15693SampleFromReader(uint8_t bit, DecodeReader_t* DecodeReader) +{ + switch(DecodeReader->state) { + case STATE_READER_UNSYNCD: + if(!bit) { + // we went low, so this could be the beginning of a SOF + DecodeReader->state = STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF; + DecodeReader->posCount = 1; + } + break; + + case STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF: + DecodeReader->posCount++; + if(bit) { // detected rising edge + if(DecodeReader->posCount < 4) { // rising edge too early (nominally expected at 5) + DecodeReader->state = STATE_READER_UNSYNCD; + } else { // SOF + DecodeReader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; } } else { - prev = b; + if(DecodeReader->posCount > 5) { // stayed low for too long + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + // do nothing, keep waiting + } } + break; - getNext = !getNext; - } - } - - ////////////////////////////////////////// - /////////// DEMODULATE /////////////////// - ////////////////////////////////////////// - - int i, j; - int max = 0, maxPos=0; - - int skip = 4; - -// if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL - - // First, correlate for SOF - for(i = 0; i < 19000; i++) { - int corr = 0; - for(j = 0; j < arraylen(FrameSOF); j += skip) { - corr += FrameSOF[j]*dest[i+(j/skip)]; - } - if(corr > max) { - max = corr; - maxPos = i; - } - } -// DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); - - int k = 0; // this will be our return value - - // greg - If correlation is less than 1 then there's little point in continuing - if ((max/(arraylen(FrameSOF)/skip)) >= 1) // THIS SHOULD BE 1 - { - - i = maxPos + arraylen(FrameSOF)/skip; - - uint8_t outBuf[20]; - memset(outBuf, 0, sizeof(outBuf)); - uint8_t mask = 0x01; - for(;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; - for(j = 0; j < arraylen(Logic0); j += skip) { - corr0 += Logic0[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(Logic1); j += skip) { - corr1 += Logic1[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(FrameEOF); j += skip) { - corrEOF += FrameEOF[j]*dest[i+(j/skip)]; - } - // Even things out by the length of the target waveform. - corr0 *= 4; - corr1 *= 4; - - if(corrEOF > corr1 && corrEOF > corr0) { - // DbpString("EOF at %d", i); - break; - } else if(corr1 > corr0) { - i += arraylen(Logic1)/skip; - outBuf[k] |= mask; + case STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF: + DecodeReader->posCount++; + if(!bit) { // detected a falling edge + if (DecodeReader->posCount < 20) { // falling edge too early (nominally expected at 21 earliest) + DecodeReader->state = STATE_READER_UNSYNCD; + } else if (DecodeReader->posCount < 23) { // SOF for 1 out of 4 coding + DecodeReader->Coding = CODING_1_OUT_OF_4; + DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } else if (DecodeReader->posCount < 28) { // falling edge too early (nominally expected at 29 latest) + DecodeReader->state = STATE_READER_UNSYNCD; + } else { // SOF for 1 out of 4 coding + DecodeReader->Coding = CODING_1_OUT_OF_256; + DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } } else { - i += arraylen(Logic0)/skip; + if(DecodeReader->posCount > 29) { // stayed high for too long + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + // do nothing, keep waiting + } } - mask <<= 1; - if(mask == 0) { - k++; - mask = 0x01; + break; + + case STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF: + DecodeReader->posCount++; + if (bit) { // detected rising edge + if (DecodeReader->Coding == CODING_1_OUT_OF_256) { + if (DecodeReader->posCount < 32) { // rising edge too early (nominally expected at 33) + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + DecodeReader->posCount = 1; + DecodeReader->bitCount = 0; + DecodeReader->byteCount = 0; + DecodeReader->sum1 = 1; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + LED_B_ON(); + } + } else { // CODING_1_OUT_OF_4 + if (DecodeReader->posCount < 24) { // rising edge too early (nominally expected at 25) + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + DecodeReader->state = STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4; + } + } + } else { + if (DecodeReader->Coding == CODING_1_OUT_OF_256) { + if (DecodeReader->posCount > 34) { // signal stayed low for too long + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + // do nothing, keep waiting + } + } else { // CODING_1_OUT_OF_4 + if (DecodeReader->posCount > 26) { // signal stayed low for too long + DecodeReader->state = STATE_READER_UNSYNCD; + } else { + // do nothing, keep waiting + } + } } - if((i+(int)arraylen(FrameEOF)) >= 2000) { - DbpString("ran off end!"); + break; + + case STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4: + DecodeReader->posCount++; + if (bit) { + if (DecodeReader->posCount == 33) { + DecodeReader->posCount = 1; + DecodeReader->bitCount = 0; + DecodeReader->byteCount = 0; + DecodeReader->sum1 = 1; + DecodeReader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + LED_B_ON(); + } else { + // do nothing, keep waiting + } + } else { // unexpected falling edge + DecodeReader->state = STATE_READER_UNSYNCD; + } + break; + + case STATE_READER_RECEIVE_DATA_1_OUT_OF_4: + DecodeReader->posCount++; + if (DecodeReader->posCount == 1) { + DecodeReader->sum1 = bit; + } else if (DecodeReader->posCount <= 4) { + DecodeReader->sum1 += bit; + } else if (DecodeReader->posCount == 5) { + DecodeReader->sum2 = bit; + } else { + DecodeReader->sum2 += bit; + } + if (DecodeReader->posCount == 8) { + DecodeReader->posCount = 0; + int corr10 = DecodeReader->sum1 - DecodeReader->sum2; + int corr01 = DecodeReader->sum2 - DecodeReader->sum1; + int corr11 = (DecodeReader->sum1 + DecodeReader->sum2) / 2; + if (corr01 > corr11 && corr01 > corr10) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReader->state = STATE_READER_UNSYNCD; + if (DecodeReader->byteCount != 0) { + return true; + } + } + if (corr10 > corr11) { // detected a 2bit position + DecodeReader->shiftReg >>= 2; + DecodeReader->shiftReg |= (DecodeReader->bitCount << 6); + } + if (DecodeReader->bitCount == 15) { // we have a full byte + DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; + if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReader->state = STATE_READER_UNSYNCD; + } + DecodeReader->bitCount = 0; + } else { + DecodeReader->bitCount++; + } + } + break; + + case STATE_READER_RECEIVE_DATA_1_OUT_OF_256: + DecodeReader->posCount++; + if (DecodeReader->posCount == 1) { + DecodeReader->sum1 = bit; + } else if (DecodeReader->posCount <= 4) { + DecodeReader->sum1 += bit; + } else if (DecodeReader->posCount == 5) { + DecodeReader->sum2 = bit; + } else { + DecodeReader->sum2 += bit; + } + if (DecodeReader->posCount == 8) { + DecodeReader->posCount = 0; + int corr10 = DecodeReader->sum1 - DecodeReader->sum2; + int corr01 = DecodeReader->sum2 - DecodeReader->sum1; + int corr11 = (DecodeReader->sum1 + DecodeReader->sum2) / 2; + if (corr01 > corr11 && corr01 > corr10) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReader->state = STATE_READER_UNSYNCD; + if (DecodeReader->byteCount != 0) { + return true; + } + } + if (corr10 > corr11) { // detected the bit position + DecodeReader->shiftReg = DecodeReader->bitCount; + } + if (DecodeReader->bitCount == 255) { // we have a full byte + DecodeReader->output[DecodeReader->byteCount++] = DecodeReader->shiftReg; + if (DecodeReader->byteCount > DecodeReader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReader->state = STATE_READER_UNSYNCD; + } + } + DecodeReader->bitCount++; + } + break; + + default: + LED_B_OFF(); + DecodeReader->state = STATE_READER_UNSYNCD; + break; + } + + return false; +} + + +static void DecodeReaderInit(uint8_t *data, uint16_t max_len, DecodeReader_t* DecodeReader) +{ + DecodeReader->output = data; + DecodeReader->byteCountMax = max_len; + DecodeReader->state = STATE_READER_UNSYNCD; + DecodeReader->byteCount = 0; + DecodeReader->bitCount = 0; + DecodeReader->posCount = 0; + DecodeReader->shiftReg = 0; +} + + +//----------------------------------------------------------------------------- +// Receive a command (from the reader to us, where we are the simulated tag), +// and store it in the given buffer, up to the given maximum length. Keeps +// spinning, waiting for a well-framed command, until either we get one +// (returns true) or someone presses the pushbutton on the board (false). +// +// Assume that we're called with the SSC (to the FPGA) and ADC path set +// correctly. +//----------------------------------------------------------------------------- + +static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) +{ + int maxBehindBy = 0; + int lastRxCounter, samples = 0; + bool gotFrame = false; + uint8_t b; + + uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; + + // the decoder data structure + DecodeReader_t DecodeReader = {0}; + DecodeReaderInit(received, max_len, &DecodeReader); + + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + + // clear receive register and wait for next transfer + uint32_t temp = AT91C_BASE_SSC->SSC_RHR; + (void) temp; + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) ; + + uint32_t bit_time = GetCountSspClk() & 0xfffffff8; + + // Setup and start DMA. + FpgaSetupSscDma(dmaBuf, ISO15693_DMA_BUFFER_SIZE); + uint8_t *upTo = dmaBuf; + lastRxCounter = ISO15693_DMA_BUFFER_SIZE; + + for(;;) { + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO15693_DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; + } + + if (behindBy < 1) continue; + + b = *upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + lastRxCounter += ISO15693_DMA_BUFFER_SIZE; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO15693_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + + for (int i = 7; i >= 0; i--) { + if (Handle15693SampleFromReader((b >> i) & 0x01, &DecodeReader)) { + *eof_time = bit_time + samples - DELAY_READER_TO_ARM; // end of EOF + gotFrame = true; break; } + samples++; } - if(mask != 0x01) { - DbpString("sniff: error, uneven octet! (discard extra bits!)"); - /// DbpString(" mask=%02x", mask); - } - // uint8_t str1 [8]; - // itoa(k,str1); - // strncat(str1," octets read",8); - - // DbpString( str1); // DbpString("%d octets", k); - - // for(i = 0; i < k; i+=3) { - // //DbpString("# %2d: %02x ", i, outBuf[i]); - // DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]); - // } - - for(i = 0; i < k; i++) { - receivedResponse[i] = outBuf[i]; - } - } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip)) - return k; // return the number of bytes demodulated -/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2)); + if (gotFrame) { + break; + } + + if (BUTTON_PRESS()) { + DecodeReader.byteCount = 0; + break; + } + + WDT_HIT(); + } + + + FpgaDisableSscDma(); + + if (DEBUG) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", + maxBehindBy, samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); + + if (tracing && DecodeReader.byteCount > 0) { + LogTrace(DecodeReader.output, DecodeReader.byteCount, 0, 0, NULL, true); + } + + return DecodeReader.byteCount; } @@ -574,11 +947,10 @@ static void BuildIdentifyRequest(void); //----------------------------------------------------------------------------- void AcquireRawAdcSamplesIso15693(void) { - uint8_t *dest = BigBuf_get_addr(); + LEDsoff(); + LED_A_ON(); - int c = 0; - int getNext = 0; - int8_t prev = 0; + uint8_t *dest = BigBuf_get_addr(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BuildIdentifyRequest(); @@ -586,151 +958,118 @@ void AcquireRawAdcSamplesIso15693(void) SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Give the tags time to energize + LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(100); // Now send the command - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - c = 0; - for(;;) { + LED_B_ON(); + for(int c = 0; c < ToSendMax; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; + AT91C_BASE_SSC->SSC_THR = ~ToSend[c]; c++; - if(c == ToSendMax+3) { - break; - } - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; } WDT_HIT(); } + LED_B_OFF(); + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - c = 0; - getNext = FALSE; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } + for(int c = 0; c < 4000; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = ABS(b) + ABS(prev); - - dest[c++] = (uint8_t)r; - - if(c >= 2000) { - break; - } - } else { - prev = b; - } - - getNext = !getNext; + // tone that the tag AM-modulates. We just want power, + // so abs(I) + abs(Q) is close to what we want. + int8_t i = (int8_t)(iq >> 8); + int8_t q = (int8_t)(iq & 0xff); + uint8_t r = AMPLITUDE(i, q); + dest[c++] = r; } } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } +// TODO: there is no trigger condition. The 14000 samples represent a time frame of 66ms. +// It is unlikely that we get something meaningful. +// TODO: Currently we only record tag answers. Add tracing of reader commands. +// TODO: would we get something at all? The carrier is switched on... void RecordRawAdcSamplesIso15693(void) { - uint8_t *dest = BigBuf_get_addr(); + LEDsoff(); + LED_A_ON(); - int c = 0; - int getNext = 0; - int8_t prev = 0; + uint8_t *dest = BigBuf_get_addr(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); SpinDelay(100); + LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - c = 0; - getNext = FALSE; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } + for(int c = 0; c < 14000;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = ABS(b) + ABS(prev); - - dest[c++] = (uint8_t)r; - - if(c >= 7000) { - break; - } - } else { - prev = b; - } - - getNext = !getNext; - WDT_HIT(); + // tone that the tag AM-modulates. We just want power, + // so abs(I) + abs(Q) is close to what we want. + int8_t i = (int8_t)(iq >> 8); + int8_t q = (int8_t)(iq & 0xff); + uint8_t r = AMPLITUDE(i, q); + dest[c++] = r; } } - Dbprintf("fin record"); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + Dbprintf("finished recording"); + LED_A_OFF(); } -// Initialize the proxmark as iso15k reader +// Initialize the proxmark as iso15k reader // (this might produces glitches that confuse some tags -void Iso15693InitReader() { - LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); - +static void Iso15693InitReader() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC // FpgaSetupSsc(); // Start from off (no field generated) + LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(10); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Give the tags time to energize + LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(250); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); } /////////////////////////////////////////////////////////////////////// // ISO 15693 Part 3 - Air Interface -// This section basicly contains transmission and receiving of bits +// This section basically contains transmission and receiving of bits /////////////////////////////////////////////////////////////////////// // Encode (into the ToSend buffers) an identify request, which is the first @@ -780,23 +1119,22 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) // Block number to read cmd[10] = blockNumber;//0x00; //Now the CRC - crc = Crc(cmd, 11); // the crc needs to be calculated over 12 bytes + crc = Crc(cmd, 11); // the crc needs to be calculated over 11 bytes cmd[11] = crc & 0xff; cmd[12] = crc >> 8; CodeIso15693AsReader(cmd, sizeof(cmd)); } + // Now the VICC>VCD responses when we are simulating a tag - static void BuildInventoryResponse( uint8_t *uid) +static void BuildInventoryResponse(uint8_t *uid) { uint8_t cmd[12]; uint16_t crc; - // one sub-carrier, inventory, 1 slot, fast rate - // AFI is at bit 5 (1<<4) when doing an INVENTORY - //(1 << 2) | (1 << 5) | (1 << 1); - cmd[0] = 0; // + + cmd[0] = 0; // No error, no protocol format extension cmd[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported // 64-bit UID cmd[2] = uid[7]; //0x32; @@ -812,31 +1150,25 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) cmd[10] = crc & 0xff; cmd[11] = crc >> 8; - CodeIso15693AsReader(cmd, sizeof(cmd)); + CodeIso15693AsTag(cmd, sizeof(cmd)); } // Universal Method for sending to and recv bytes from a tag // init ... should we initialize the reader? -// speed ... 0 low speed, 1 hi speed +// speed ... 0 low speed, 1 hi speed // **recv will return you a pointer to the received data -// If you do not need the answer use NULL for *recv[] +// If you do not need the answer use NULL for *recv[] // return: lenght of received data -int SendDataTag(uint8_t *send, int sendlen, int init, int speed, uint8_t **recv) { +int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t **recv) { - int samples = 0; - int tsamples = 0; - int wait = 0; - int elapsed = 0; - LED_A_ON(); - LED_B_ON(); + LED_B_OFF(); LED_C_OFF(); - LED_D_OFF(); - + if (init) Iso15693InitReader(); int answerLen=0; - uint8_t *answer = BigBuf_get_addr() + 3660; + uint8_t *answer = BigBuf_get_addr() + 4000; if (recv != NULL) memset(answer, 0, 100); if (!speed) { @@ -846,30 +1178,22 @@ int SendDataTag(uint8_t *send, int sendlen, int init, int speed, uint8_t **recv) // high speed (1 out of 4) CodeIso15693AsReader(send, sendlen); } - - LED_A_ON(); - LED_B_OFF(); - - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); + + TransmitTo15693Tag(ToSend,ToSendMax); // Now wait for a response if (recv!=NULL) { - LED_A_OFF(); - LED_B_ON(); - answerLen = GetIso15693AnswerFromTag(answer, 100, &samples, &elapsed) ; + answerLen = GetIso15693AnswerFromTag(answer, 100); *recv=answer; } LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); - + return answerLen; } // -------------------------------------------------------------------- -// Debug Functions +// Debug Functions // -------------------------------------------------------------------- // Decodes a message from a tag and displays its metadata and content @@ -879,37 +1203,37 @@ void DbdecodeIso15693Answer(int len, uint8_t *d) { uint16_t crc; if (len>3) { - if (d[0]&(1<<3)) + if (d[0]&(1<<3)) strncat(status,"ProtExt ",DBD15STATLEN); - if (d[0]&1) { + if (d[0]&1) { // error strncat(status,"Error ",DBD15STATLEN); switch (d[1]) { - case 0x01: + case 0x01: strncat(status,"01:notSupp",DBD15STATLEN); break; - case 0x02: + case 0x02: strncat(status,"02:notRecog",DBD15STATLEN); break; - case 0x03: + case 0x03: strncat(status,"03:optNotSupp",DBD15STATLEN); break; - case 0x0f: + case 0x0f: strncat(status,"0f:noInfo",DBD15STATLEN); break; - case 0x10: + case 0x10: strncat(status,"10:dontExist",DBD15STATLEN); break; - case 0x11: + case 0x11: strncat(status,"11:lockAgain",DBD15STATLEN); break; - case 0x12: + case 0x12: strncat(status,"12:locked",DBD15STATLEN); break; - case 0x13: + case 0x13: strncat(status,"13:progErr",DBD15STATLEN); break; - case 0x14: + case 0x14: strncat(status,"14:lockErr",DBD15STATLEN); break; default: @@ -919,12 +1243,12 @@ void DbdecodeIso15693Answer(int len, uint8_t *d) { } else { strncat(status,"NoErr ",DBD15STATLEN); } - + crc=Crc(d,len-2); - if ( (( crc & 0xff ) == d[len-2]) && (( crc >> 8 ) == d[len-1]) ) + if ( (( crc & 0xff ) == d[len-2]) && (( crc >> 8 ) == d[len-1]) ) strncat(status,"CrcOK",DBD15STATLEN); else - strncat(status,"CrcFail!",DBD15STATLEN); + strncat(status,"CrcFail!",DBD15STATLEN); Dbprintf("%s",status); } @@ -942,64 +1266,46 @@ void SetDebugIso15693(uint32_t debug) { return; } - - //----------------------------------------------------------------------------- // Simulate an ISO15693 reader, perform anti-collision and then attempt to read a sector // all demodulation performed in arm rather than host. - greg //----------------------------------------------------------------------------- void ReaderIso15693(uint32_t parameter) { + LEDsoff(); LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); int answerLen1 = 0; - int answerLen2 = 0; - int answerLen3 = 0; - int i = 0; - int samples = 0; - int tsamples = 0; - int wait = 0; - int elapsed = 0; uint8_t TagUID[8] = {0x00}; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - uint8_t *answer1 = BigBuf_get_addr() + 3660; - uint8_t *answer2 = BigBuf_get_addr() + 3760; - uint8_t *answer3 = BigBuf_get_addr() + 3860; - // Blank arrays - memset(answer1, 0x00, 300); + uint8_t *answer1 = BigBuf_get_addr() + 4000; + memset(answer1, 0x00, 200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(200); // Give the tags time to energize + LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(200); - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); - // FIRST WE RUN AN INVENTORY TO GET THE TAG UID // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME // Now send the IDENTIFY command BuildIdentifyRequest(); - - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); - + + TransmitTo15693Tag(ToSend,ToSendMax); + // Now wait for a response - answerLen1 = GetIso15693AnswerFromTag(answer1, 100, &samples, &elapsed) ; + answerLen1 = GetIso15693AnswerFromTag(answer1, 100) ; if (answerLen1 >=12) // we should do a better check than this { @@ -1015,178 +1321,169 @@ void ReaderIso15693(uint32_t parameter) } Dbprintf("%d octets read from IDENTIFY request:", answerLen1); - DbdecodeIso15693Answer(answerLen1,answer1); - Dbhexdump(answerLen1,answer1,true); + DbdecodeIso15693Answer(answerLen1, answer1); + Dbhexdump(answerLen1, answer1, false); // UID is reverse - if (answerLen1>=12) + if (answerLen1 >= 12) Dbprintf("UID = %02hX%02hX%02hX%02hX%02hX%02hX%02hX%02hX", TagUID[7],TagUID[6],TagUID[5],TagUID[4], TagUID[3],TagUID[2],TagUID[1],TagUID[0]); - Dbprintf("%d octets read from SELECT request:", answerLen2); - DbdecodeIso15693Answer(answerLen2,answer2); - Dbhexdump(answerLen2,answer2,true); + // Dbprintf("%d octets read from SELECT request:", answerLen2); + // DbdecodeIso15693Answer(answerLen2,answer2); + // Dbhexdump(answerLen2,answer2,true); - Dbprintf("%d octets read from XXX request:", answerLen3); - DbdecodeIso15693Answer(answerLen3,answer3); - Dbhexdump(answerLen3,answer3,true); + // Dbprintf("%d octets read from XXX request:", answerLen3); + // DbdecodeIso15693Answer(answerLen3,answer3); + // Dbhexdump(answerLen3,answer3,true); // read all pages - if (answerLen1>=12 && DEBUG) { - i=0; - while (i<32) { // sanity check, assume max 32 pages - BuildReadBlockRequest(TagUID,i); - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); - answerLen2 = GetIso15693AnswerFromTag(answer2, 100, &samples, &elapsed); - if (answerLen2>0) { - Dbprintf("READ SINGLE BLOCK %d returned %d octets:",i,answerLen2); - DbdecodeIso15693Answer(answerLen2,answer2); - Dbhexdump(answerLen2,answer2,true); - if ( *((uint32_t*) answer2) == 0x07160101 ) break; // exit on NoPageErr - } + if (answerLen1 >= 12 && DEBUG) { + uint8_t *answer2 = BigBuf_get_addr() + 4100; + int i = 0; + while (i < 32) { // sanity check, assume max 32 pages + BuildReadBlockRequest(TagUID, i); + TransmitTo15693Tag(ToSend, ToSendMax); + int answerLen2 = GetIso15693AnswerFromTag(answer2, 100); + if (answerLen2 > 0) { + Dbprintf("READ SINGLE BLOCK %d returned %d octets:", i, answerLen2); + DbdecodeIso15693Answer(answerLen2, answer2); + Dbhexdump(answerLen2, answer2, false); + if ( *((uint32_t*) answer2) == 0x07160101 ) break; // exit on NoPageErr + } i++; - } + } } - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); + // for the time being, switch field off to protect rdv4.0 + // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); + + LED_A_OFF(); } -// Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands -// all demodulation performed in arm rather than host. - greg + +// Simulate an ISO15693 TAG. +// For Inventory command: print command and send Inventory Response with given UID +// TODO: interpret other reader commands and send appropriate response void SimTagIso15693(uint32_t parameter, uint8_t *uid) { + LEDsoff(); LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); - - int answerLen1 = 0; - int samples = 0; - int tsamples = 0; - int wait = 0; - int elapsed = 0; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - uint8_t *buf = BigBuf_get_addr() + 3660; - memset(buf, 0x00, 100); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); - // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); + StartCountSspClk(); - LED_A_OFF(); - LED_B_OFF(); - LED_C_ON(); - LED_D_OFF(); + uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; + + // Build a suitable response to the reader INVENTORY command + BuildInventoryResponse(uid); // Listen to reader - answerLen1 = GetIso15693AnswerFromSniff(buf, 100, &samples, &elapsed) ; + while (!BUTTON_PRESS()) { + uint32_t eof_time = 0, start_time = 0; + int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &eof_time); - if (answerLen1 >=1) // we should do a better check than this - { - // Build a suitable reponse to the reader INVENTORY cocmmand - // not so obsvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below. - - BuildInventoryResponse(uid); - - TransmitTo15693Reader(ToSend,ToSendMax, &tsamples, &wait); + if ((cmd_len >= 5) && (cmd[0] & ISO15693_REQ_INVENTORY) && (cmd[1] == ISO15693_INVENTORY)) { // TODO: check more flags + bool slow = !(cmd[0] & ISO15693_REQ_DATARATE_HIGH); + start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC - DELAY_ARM_TO_READER; + TransmitTo15693Reader(ToSend, ToSendMax, start_time, slow); + } + + Dbprintf("%d bytes read from reader:", cmd_len); + Dbhexdump(cmd_len, cmd, false); } - Dbprintf("%d octets read from reader command: %x %x %x %x %x %x %x %x %x", answerLen1, - buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6], buf[7], buf[8]); - - Dbprintf("Simulationg uid: %x %x %x %x %x %x %x %x", - uid[0], uid[1], uid[2], uid[3], - uid[4], uid[5], uid[6], uid[7]); - - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); + LEDsoff(); } // Since there is no standardized way of reading the AFI out of a tag, we will brute force it // (some manufactures offer a way to read the AFI, though) -void BruteforceIso15693Afi(uint32_t speed) -{ +void BruteforceIso15693Afi(uint32_t speed) +{ + LEDsoff(); + LED_A_ON(); + uint8_t data[20]; uint8_t *recv=data; int datalen=0, recvlen=0; - + Iso15693InitReader(); - + // first without AFI - // Tags should respond wihtout AFI and with AFI=0 even when AFI is active - - data[0]=ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - data[1]=ISO15_CMD_INVENTORY; - data[2]=0; // mask length - datalen=AddCrc(data,3); - recvlen=SendDataTag(data,datalen,0,speed,&recv); + // Tags should respond without AFI and with AFI=0 even when AFI is active + + data[0] = ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1; + data[1] = ISO15693_INVENTORY; + data[2] = 0; // mask length + datalen = AddCrc(data,3); + recvlen = SendDataTag(data, datalen, false, speed, &recv); WDT_HIT(); if (recvlen>=12) { Dbprintf("NoAFI UID=%s",sprintUID(NULL,&recv[2])); } - + // now with AFI - - data[0]=ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_AFI | ISO15_REQINV_SLOT1; - data[1]=ISO15_CMD_INVENTORY; - data[2]=0; // AFI - data[3]=0; // mask length - + + data[0] = ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_AFI | ISO15693_REQINV_SLOT1; + data[1] = ISO15693_INVENTORY; + data[2] = 0; // AFI + data[3] = 0; // mask length + for (int i=0;i<256;i++) { data[2]=i & 0xFF; datalen=AddCrc(data,4); - recvlen=SendDataTag(data,datalen,0,speed,&recv); + recvlen=SendDataTag(data, datalen, false, speed, &recv); WDT_HIT(); if (recvlen>=12) { - Dbprintf("AFI=%i UID=%s",i,sprintUID(NULL,&recv[2])); + Dbprintf("AFI=%i UID=%s", i, sprintUID(NULL,&recv[2])); } - } + } Dbprintf("AFI Bruteforcing done."); - + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } // Allows to directly send commands to the tag via the client -void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8_t data[]) { +void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t data[]) { int recvlen=0; uint8_t *recvbuf = BigBuf_get_addr(); -// UsbCommand n; - + + LED_A_ON(); + if (DEBUG) { Dbprintf("SEND"); - Dbhexdump(datalen,data,true); + Dbhexdump(datalen, data, false); } - - recvlen=SendDataTag(data,datalen,1,speed,(recv?&recvbuf:NULL)); - if (recv) { - LED_B_ON(); - cmd_send(CMD_ACK,recvlen>48?48:recvlen,0,0,recvbuf,48); - LED_B_OFF(); - + recvlen = SendDataTag(data, datalen, true, speed, (recv?&recvbuf:NULL)); + + if (recv) { + cmd_send(CMD_ACK, recvlen>48?48:recvlen, 0, 0, recvbuf, 48); + if (DEBUG) { Dbprintf("RECV"); - DbdecodeIso15693Answer(recvlen,recvbuf); - Dbhexdump(recvlen,recvbuf,true); + DbdecodeIso15693Answer(recvlen,recvbuf); + Dbhexdump(recvlen, recvbuf, false); } } + // for the time being, switch field off to protect rdv4.0 + // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + + LED_A_OFF(); } diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 27dcc297..d3fd35d1 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -1,5 +1,7 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz +// 2016 Iceman +// 2018 AntiCat // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -15,745 +17,448 @@ #include "legicrf.h" #include "legic_prng.h" +#include "legic.h" #include "crc.h" -static struct legic_frame { - int bits; - uint32_t data; -} current_frame; +static legic_card_select_t card;/* metadata of currently selected card */ +static crc_t legic_crc; -static enum { - STATE_DISCON, - STATE_IV, - STATE_CON, -} legic_state; +//----------------------------------------------------------------------------- +// Frame timing and pseudorandom number generator +// +// The Prng is forwarded every 100us (TAG_BIT_PERIOD), except when the reader is +// transmitting. In that case the prng has to be forwarded every bit transmitted: +// - 60us for a 0 (RWD_TIME_0) +// - 100us for a 1 (RWD_TIME_1) +// +// The data dependent timing makes writing comprehensible code significantly +// harder. The current aproach forwards the prng data based if there is data on +// air and time based, using GET_TICKS, during computational and wait periodes. +// +// To not have the necessity to calculate/guess exection time dependend timeouts +// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots. +//----------------------------------------------------------------------------- -static crc_t legic_crc; -static int legic_read_count; -static uint32_t legic_prng_bc; -static uint32_t legic_prng_iv; +static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ -static int legic_phase_drift; -static int legic_frame_drift; -static int legic_reqresp_drift; +#define RWD_TIME_PAUSE 30 /* 20us */ +#define RWD_TIME_1 150 /* READER_TIME_PAUSE 20us off + 80us on = 100us */ +#define RWD_TIME_0 90 /* READER_TIME_PAUSE 20us off + 40us on = 60us */ +#define RWD_FRAME_WAIT 330 /* 220us from TAG frame end to READER frame start */ +#define TAG_FRAME_WAIT 495 /* 330us from READER frame end to TAG frame start */ +#define TAG_BIT_PERIOD 150 /* 100us */ +#define TAG_WRITE_TIMEOUT 60 /* 40 * 100us (write should take at most 3.6ms) */ -AT91PS_TC timer; -AT91PS_TC prng_timer; +#define LEGIC_READ 0x01 /* Read Command */ +#define LEGIC_WRITE 0x00 /* Write Command */ -static void setup_timer(void) -{ - /* Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging - * this it won't be terribly accurate but should be good enough. - */ - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); - timer = AT91C_BASE_TC1; - timer->TC_CCR = AT91C_TC_CLKDIS; - timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; +#define SESSION_IV 0x55 /* An arbitrary chose session IV, all shoud work */ +#define OFFSET_LOG 1024 /* The largest Legic Prime card is 1k */ +#define WRITE_LOWERLIMIT 4 /* UID and MCC are not writable */ - /* - * Set up Timer 2 to use for measuring time between frames in - * tag simulation mode. Runs 4x faster as Timer 1 - */ - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC2); - prng_timer = AT91C_BASE_TC2; - prng_timer->TC_CCR = AT91C_TC_CLKDIS; - prng_timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV2_CLOCK; - prng_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; +#define INPUT_THRESHOLD 8 /* heuristically determined, lower values */ + /* lead to detecting false ack during write */ + +//----------------------------------------------------------------------------- +// I/O interface abstraction (FPGA -> ARM) +//----------------------------------------------------------------------------- + +static inline uint16_t rx_frame_from_fpga() { + for(;;) { + WDT_HIT(); + + // wait for frame be become available in rx holding register + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + return AT91C_BASE_SSC->SSC_RHR; + } + } } -/* At TIMER_CLOCK3 (MCK/32) */ -#define RWD_TIME_1 150 /* RWD_TIME_PAUSE off, 80us on = 100us */ -#define RWD_TIME_0 90 /* RWD_TIME_PAUSE off, 40us on = 60us */ -#define RWD_TIME_PAUSE 30 /* 20us */ -#define RWD_TIME_FUZZ 20 /* rather generous 13us, since the peak detector + hysteresis fuzz quite a bit */ -#define TAG_TIME_BIT 150 /* 100us for every bit */ -#define TAG_TIME_WAIT 490 /* time from RWD frame end to tag frame start, experimentally determined */ +//----------------------------------------------------------------------------- +// Demodulation (Reader) +//----------------------------------------------------------------------------- -#define SIM_DIVISOR 586 /* prng_time/SIM_DIVISOR count prng needs to be forwared */ -#define SIM_SHIFT 900 /* prng_time+SIM_SHIFT shift of delayed start */ +// Returns a demedulated bit +// +// The FPGA running xcorrelation samples the subcarrier at ~13.56 MHz. The mode +// was initialy designed to receive BSPK/2-PSK. Hance, it reports an I/Q pair +// every 4.7us (8 bits i and 8 bits q). +// +// The subcarrier amplitude can be calculated using Pythagoras sqrt(i^2 + q^2). +// To reduce CPU time the amplitude is approximated by using linear functions: +// am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq)) +// +// The bit time is 99.1us (21 I/Q pairs). The receiver skips the first 5 samples +// and averages the next (most stable) 8 samples. The final 8 samples are dropped +// also. +// +// The demodulated should be alligned to the bit period by the caller. This is +// done in rx_bit and rx_ack. +static inline bool rx_bit() { + int32_t sum_cq = 0; + int32_t sum_ci = 0; -#define SESSION_IV 0x55 -#define OFFSET_LOG 1024 - -#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz))) - -/* Generate Keystream */ -static uint32_t get_key_stream(int skip, int count) -{ - uint32_t key=0; int i; - - /* Use int to enlarge timer tc to 32bit */ - legic_prng_bc += prng_timer->TC_CV; - prng_timer->TC_CCR = AT91C_TC_SWTRG; - - /* If skip == -1, forward prng time based */ - if(skip == -1) { - i = (legic_prng_bc+SIM_SHIFT)/SIM_DIVISOR; /* Calculate Cycles based on timer */ - i -= legic_prng_count(); /* substract cycles of finished frames */ - i -= count; /* substract current frame length, rewidn to bedinning */ - legic_prng_forward(i); - } else { - legic_prng_forward(skip); + // skip first 5 I/Q pairs + for(size_t i = 0; i<5; ++i) { + (void)rx_frame_from_fpga(); } - /* Write Time Data into LOG */ - uint8_t *BigBuf = BigBuf_get_addr(); - if(count == 6) { i = -1; } else { i = legic_read_count; } - BigBuf[OFFSET_LOG+128+i] = legic_prng_count(); - BigBuf[OFFSET_LOG+256+i*4] = (legic_prng_bc >> 0) & 0xff; - BigBuf[OFFSET_LOG+256+i*4+1] = (legic_prng_bc >> 8) & 0xff; - BigBuf[OFFSET_LOG+256+i*4+2] = (legic_prng_bc >>16) & 0xff; - BigBuf[OFFSET_LOG+256+i*4+3] = (legic_prng_bc >>24) & 0xff; - BigBuf[OFFSET_LOG+384+i] = count; + // sample next 8 I/Q pairs + for(size_t i = 0; i<8; ++i) { + uint16_t iq = rx_frame_from_fpga(); + int8_t ci = (int8_t)(iq >> 8); + int8_t cq = (int8_t)(iq & 0xff); + sum_ci += ci; + sum_cq += cq; + } - /* Generate KeyStream */ - for(i=0; i> 3) > INPUT_THRESHOLD); +} + +//----------------------------------------------------------------------------- +// Modulation (Reader) +// +// I've tried to modulate the Legic specific pause-puls using ssc and the default +// ssc clock of 105.4 kHz (bit periode of 9.4us) - previous commit. However, +// the timing was not precise enough. By increasing the ssc clock this could +// be circumvented, but the adventage over bitbang would be little. +//----------------------------------------------------------------------------- + +static inline void tx_bit(bool bit) { + // insert pause + HIGH(GPIO_SSC_DOUT); + last_frame_end += RWD_TIME_PAUSE; + while(GET_TICKS < last_frame_end) { }; + + // return to carrier on, wait for bit periode to end + LOW(GPIO_SSC_DOUT); + last_frame_end += (bit ? RWD_TIME_1 : RWD_TIME_0) - RWD_TIME_PAUSE; + while(GET_TICKS < last_frame_end) { }; +} + +//----------------------------------------------------------------------------- +// Frame Handling (Reader) +// +// The LEGIC RF protocol from card to reader does not include explicit frame +// start/stop information or length information. The reader must know beforehand +// how many bits it wants to receive. +// Notably: a card sending a stream of 0-bits is indistinguishable from no card +// present. +//----------------------------------------------------------------------------- + +static void tx_frame(uint32_t frame, uint8_t len) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + + // wait for next tx timeslot + last_frame_end += RWD_FRAME_WAIT; + while(GET_TICKS < last_frame_end) { }; + + // transmit frame, MSB first + for(uint8_t i = 0; i < len; ++i) { + bool bit = (frame >> i) & 0x01; + tx_bit(bit ^ legic_prng_get_bit()); legic_prng_forward(1); + }; + + // add pause to mark end of the frame + HIGH(GPIO_SSC_DOUT); + last_frame_end += RWD_TIME_PAUSE; + while(GET_TICKS < last_frame_end) { }; + LOW(GPIO_SSC_DOUT); +} + +static uint32_t rx_frame(uint8_t len) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + + // hold sampling until card is expected to respond + last_frame_end += TAG_FRAME_WAIT; + while(GET_TICKS < last_frame_end) { }; + + uint32_t frame = 0; + for(uint8_t i = 0; i < len; ++i) { + frame |= (rx_bit() ^ legic_prng_get_bit()) << i; + legic_prng_forward(1); + + // rx_bit runs only 95us, resync to TAG_BIT_PERIOD + last_frame_end += TAG_BIT_PERIOD; + while(GET_TICKS < last_frame_end) { }; } - return key; + + return frame; } -/* Send a frame in tag mode, the FPGA must have been set up by - * LegicRfSimulate - */ -static void frame_send_tag(uint16_t response, int bits, int crypt) -{ - /* Bitbang the response */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - - /* Use time to crypt frame */ - if(crypt) { - legic_prng_forward(2); /* TAG_TIME_WAIT -> shift by 2 */ - int i; int key = 0; - for(i=0; iTC_CV < (TAG_TIME_WAIT - 30)) ; - - int i; - for(i=0; iTC_CV + TAG_TIME_BIT; - int bit = response & 1; - response = response >> 1; - if(bit) { - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - } else { - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - } - while(timer->TC_CV < nextbit) ; - } - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; -} + // hold sampling until card is expected to respond + last_frame_end += TAG_FRAME_WAIT; + while(GET_TICKS < last_frame_end) { }; -/* Send a frame in reader mode, the FPGA must have been set up by - * LegicRfReader - */ -static void frame_send_rwd(uint32_t data, int bits) -{ - /* Start clock */ - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ + uint32_t ack = 0; + for(uint8_t i = 0; i < TAG_WRITE_TIMEOUT; ++i) { + // sample bit + ack = rx_bit(); + legic_prng_forward(1); - int i; - for(i=0; iTC_CV; - int pause_end = starttime + RWD_TIME_PAUSE, bit_end; - int bit = data & 1; - data = data >> 1; + // rx_bit runs only 95us, resync to TAG_BIT_PERIOD + last_frame_end += TAG_BIT_PERIOD; + while(GET_TICKS < last_frame_end) { }; - if(bit ^ legic_prng_get_bit()) { - bit_end = starttime + RWD_TIME_1; - } else { - bit_end = starttime + RWD_TIME_0; - } - - /* RWD_TIME_PAUSE time off, then some time on, so that the complete bit time is - * RWD_TIME_x, where x is the bit to be transmitted */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - while(timer->TC_CV < pause_end) ; - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - legic_prng_forward(1); /* bit duration is longest. use this time to forward the lfsr */ - - while(timer->TC_CV < bit_end) ; - } - - { - /* One final pause to mark the end of the frame */ - int pause_end = timer->TC_CV + RWD_TIME_PAUSE; - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - while(timer->TC_CV < pause_end) ; - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - } - - /* Reset the timer, to measure time until the start of the tag frame */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ -} - -/* Receive a frame from the card in reader emulation mode, the FPGA and - * timer must have been set up by LegicRfReader and frame_send_rwd. - * - * The LEGIC RF protocol from card to reader does not include explicit - * frame start/stop information or length information. The reader must - * know beforehand how many bits it wants to receive. (Notably: a card - * sending a stream of 0-bits is indistinguishable from no card present.) - * - * Receive methodology: There is a fancy correlator in hi_read_rx_xcorr, but - * I'm not smart enough to use it. Instead I have patched hi_read_tx to output - * the ADC signal with hysteresis on SSP_DIN. Bit-bang that signal and look - * for edges. Count the edges in each bit interval. If they are approximately - * 0 this was a 0-bit, if they are approximately equal to the number of edges - * expected for a 212kHz subcarrier, this was a 1-bit. For timing we use the - * timer that's still running from frame_send_rwd in order to get a synchronization - * with the frame that we just sent. - * - * FIXME: Because we're relying on the hysteresis to just do the right thing - * the range is severely reduced (and you'll probably also need a good antenna). - * So this should be fixed some time in the future for a proper receiver. - */ -static void frame_receive_rwd(struct legic_frame * const f, int bits, int crypt) -{ - uint32_t the_bit = 1; /* Use a bitmask to save on shifts */ - uint32_t data=0; - int i, old_level=0, edges=0; - int next_bit_at = TAG_TIME_WAIT; - - if(bits > 32) { - bits = 32; + // check if it was an ACK + if(ack) { + break; } + } - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; - - /* we have some time now, precompute the cipher - * since we cannot compute it on the fly while reading */ - legic_prng_forward(2); - - if(crypt) - { - for(i=0; iTC_CV < next_bit_at) ; - - next_bit_at += TAG_TIME_BIT; - - for(i=0; iTC_CV < next_bit_at) { - int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - if(level != old_level) - edges++; - old_level = level; - } - next_bit_at += TAG_TIME_BIT; - - if(edges > 20 && edges < 60) { /* expected are 42 edges */ - data ^= the_bit; - } - the_bit <<= 1; - } - - f->data = data; - f->bits = bits; - - /* Reset the timer, to synchronize the next frame */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ + return ack; } -static void frame_append_bit(struct legic_frame * const f, int bit) -{ - if(f->bits >= 31) { - return; /* Overflow, won't happen */ - } - f->data |= (bit<bits); - f->bits++; +//----------------------------------------------------------------------------- +// Legic Reader +//----------------------------------------------------------------------------- + +static int init_card(uint8_t cardtype, legic_card_select_t *p_card) { + p_card->tagtype = cardtype; + + switch(p_card->tagtype) { + case 0x0d: + p_card->cmdsize = 6; + p_card->addrsize = 5; + p_card->cardsize = 22; + break; + case 0x1d: + p_card->cmdsize = 9; + p_card->addrsize = 8; + p_card->cardsize = 256; + break; + case 0x3d: + p_card->cmdsize = 11; + p_card->addrsize = 10; + p_card->cardsize = 1024; + break; + default: + p_card->cmdsize = 0; + p_card->addrsize = 0; + p_card->cardsize = 0; + return 2; + } + return 0; } -static void frame_clean(struct legic_frame * const f) -{ - f->data = 0; - f->bits = 0; +static void init_reader(bool clear_mem) { + // configure FPGA + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + LED_D_ON(); + + // configure SSC with defaults + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + + // re-claim GPIO_SSC_DOUT as GPIO and enable output + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + LOW(GPIO_SSC_DOUT); + + // init crc calculator + crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0); + + // start us timer + StartTicks(); } -static uint32_t perform_setup_phase_rwd(int iv) -{ +// Setup reader to card connection +// +// The setup consists of a three way handshake: +// - Transmit initialisation vector 7 bits +// - Receive card type 6 bits +// - Transmit Acknowledge 6 bits +static uint32_t setup_phase(uint8_t iv) { + // init coordination timestamp + last_frame_end = GET_TICKS; - /* Switch on carrier and let the tag charge for 1ms */ - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - SpinDelay(1); + // Switch on carrier and let the card charge for 5ms. + last_frame_end += 7500; + while(GET_TICKS < last_frame_end) { }; - legic_prng_init(0); /* no keystream yet */ - frame_send_rwd(iv, 7); - legic_prng_init(iv); + legic_prng_init(0); + tx_frame(iv, 7); - frame_clean(¤t_frame); - frame_receive_rwd(¤t_frame, 6, 1); - legic_prng_forward(1); /* we wait anyways */ - while(timer->TC_CV < 387) ; /* ~ 258us */ - frame_send_rwd(0x19, 6); + // configure prng + legic_prng_init(iv); + legic_prng_forward(2); - return current_frame.data; + // receive card type + int32_t card_type = rx_frame(6); + legic_prng_forward(3); + + // send obsfuscated acknowledgment frame + switch (card_type) { + case 0x0D: + tx_frame(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01 + break; + case 0x1D: + case 0x3D: + tx_frame(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01 + break; + } + + return card_type; } -static void LegicCommonInit(void) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - - /* Bitbang the transmitter */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - - setup_timer(); - - crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); +static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) { + crc_clear(&legic_crc); + crc_update(&legic_crc, (value << cmd_sz) | cmd, 8 + cmd_sz); + return crc_finish(&legic_crc); } -static void switch_off_tag_rwd(void) -{ - /* Switch off carrier, make sure tag is reset */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - SpinDelay(10); +static int16_t read_byte(uint16_t index, uint8_t cmd_sz) { + uint16_t cmd = (index << 1) | LEGIC_READ; - WDT_HIT(); -} -/* calculate crc for a legic command */ -static int LegicCRC(int byte_index, int value, int cmd_sz) { - crc_clear(&legic_crc); - crc_update(&legic_crc, 1, 1); /* CMD_READ */ - crc_update(&legic_crc, byte_index, cmd_sz-1); - crc_update(&legic_crc, value, 8); - return crc_finish(&legic_crc); + // read one byte + LED_B_ON(); + legic_prng_forward(2); + tx_frame(cmd, cmd_sz); + legic_prng_forward(2); + uint32_t frame = rx_frame(12); + LED_B_OFF(); + + // split frame into data and crc + uint8_t byte = BYTEx(frame, 0); + uint8_t crc = BYTEx(frame, 1); + + // check received against calculated crc + uint8_t calc_crc = calc_crc4(cmd, cmd_sz, byte); + if(calc_crc != crc) { + Dbprintf("!!! crc mismatch: %x != %x !!!", calc_crc, crc); + return -1; + } + + legic_prng_forward(1); + + return byte; } -int legic_read_byte(int byte_index, int cmd_sz) { - int byte; +// Transmit write command, wait until (3.6ms) the tag sends back an unencrypted +// ACK ('1' bit) and forward the prng time based. +bool write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) { + uint32_t cmd = index << 1 | LEGIC_WRITE; // prepare command + uint8_t crc = calc_crc4(cmd, addr_sz + 1, byte); // calculate crc + cmd |= byte << (addr_sz + 1); // append value + cmd |= (crc & 0xF) << (addr_sz + 1 + 8); // and crc - legic_prng_forward(4); /* we wait anyways */ - while(timer->TC_CV < 387) ; /* ~ 258us + 100us*delay */ + // send write command + LED_C_ON(); + legic_prng_forward(2); + tx_frame(cmd, addr_sz + 1 + 8 + 4); // sz = addr_sz + cmd + data + crc + legic_prng_forward(3); + LED_C_OFF(); - frame_send_rwd(1 | (byte_index << 1), cmd_sz); - frame_clean(¤t_frame); - - frame_receive_rwd(¤t_frame, 12, 1); - - byte = current_frame.data & 0xff; - if( LegicCRC(byte_index, byte, cmd_sz) != (current_frame.data >> 8) ) { - Dbprintf("!!! crc mismatch: expected %x but got %x !!!", - LegicCRC(byte_index, current_frame.data & 0xff, cmd_sz), current_frame.data >> 8); - return -1; - } - - return byte; + // wait for ack + return rx_ack(); } -/* legic_write_byte() is not included, however it's trivial to implement - * and here are some hints on what remains to be done: - * - * * assemble a write_cmd_frame with crc and send it - * * wait until the tag sends back an ACK ('1' bit unencrypted) - * * forward the prng based on the timing - */ -int legic_write_byte(int byte, int addr, int addr_sz) { - //do not write UID, CRC, DCF - if(addr <= 0x06) { - return 0; - } +//----------------------------------------------------------------------------- +// Command Line Interface +// +// Only this functions are public / called from appmain.c +//----------------------------------------------------------------------------- +void LegicRfReader(int offset, int bytes) { + uint8_t *BigBuf = BigBuf_get_addr(); + memset(BigBuf, 0, 1024); - //== send write command ============================== - crc_clear(&legic_crc); - crc_update(&legic_crc, 0, 1); /* CMD_WRITE */ - crc_update(&legic_crc, addr, addr_sz); - crc_update(&legic_crc, byte, 8); + // configure ARM and FPGA + init_reader(false); - uint32_t crc = crc_finish(&legic_crc); - uint32_t cmd = ((crc <<(addr_sz+1+8)) //CRC - |(byte <<(addr_sz+1)) //Data - |(addr <<1) //Address - |(0x00 <<0)); //CMD = W - uint32_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd + // establish shared secret and detect card type + DbpString("Reading card ..."); + uint8_t card_type = setup_phase(SESSION_IV); + if(init_card(card_type, &card) != 0) { + Dbprintf("No or unknown card found, aborting"); + goto OUT; + } - legic_prng_forward(2); /* we wait anyways */ - while(timer->TC_CV < 387) {}; /* ~ 258us */ - frame_send_rwd(cmd, cmd_sz); + // if no argument is specified create full dump + if(bytes == -1) { + bytes = card.cardsize; + } - //== wait for ack ==================================== - int t, old_level=0, edges=0; - int next_bit_at =0; - while(timer->TC_CV < 387) ; /* ~ 258us */ - for(t=0; t<80; t++) { - edges = 0; - next_bit_at += TAG_TIME_BIT; - while(timer->TC_CV < next_bit_at) { - int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - if(level != old_level) { - edges++; - } - old_level = level; - } - if(edges > 20 && edges < 60) { /* expected are 42 edges */ - int t = timer->TC_CV; - int c = t/TAG_TIME_BIT; - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ - legic_prng_forward(c); - return 0; - } + // do not read beyond card memory + if(bytes + offset > card.cardsize) { + bytes = card.cardsize - offset; + } + + for(uint16_t i = 0; i < bytes; ++i) { + int16_t byte = read_byte(offset + i, card.cmdsize); + if(byte == -1) { + Dbprintf("operation failed @ 0x%03.3x", bytes); + goto OUT; } - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) {}; /* Wait till the clock has reset */ - return -1; -} + BigBuf[i] = byte; + } -int LegicRfReader(int offset, int bytes) { - int byte_index=0, cmd_sz=0, card_sz=0; + // OK + Dbprintf("Card (MIM %i) read, use 'hf legic decode' or", card.cardsize); + Dbprintf("'data hexsamples %d' to view results", (bytes+7) & ~7); - LegicCommonInit(); - - uint8_t *BigBuf = BigBuf_get_addr(); - memset(BigBuf, 0, 1024); - - DbpString("setting up legic card"); - uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); - switch_off_tag_rwd(); //we lose to mutch time with dprintf - switch(tag_type) { - case 0x1d: - DbpString("MIM 256 card found, reading card ..."); - cmd_sz = 9; - card_sz = 256; - break; - case 0x3d: - DbpString("MIM 1024 card found, reading card ..."); - cmd_sz = 11; - card_sz = 1024; - break; - default: - Dbprintf("Unknown card format: %x",tag_type); - return -1; - } - if(bytes == -1) { - bytes = card_sz; - } - if(bytes+offset >= card_sz) { - bytes = card_sz-offset; - } - - perform_setup_phase_rwd(SESSION_IV); - - LED_B_ON(); - while(byte_index < bytes) { - int r = legic_read_byte(byte_index+offset, cmd_sz); - if(r == -1 ||BUTTON_PRESS()) { - DbpString("operation aborted"); - switch_off_tag_rwd(); - LED_B_OFF(); - LED_C_OFF(); - return -1; - } - BigBuf[byte_index] = r; - WDT_HIT(); - byte_index++; - if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); - } - LED_B_OFF(); - LED_C_OFF(); - switch_off_tag_rwd(); - Dbprintf("Card read, use 'hf legic decode' or"); - Dbprintf("'data hexsamples %d' to view results", (bytes+7) & ~7); - return 0; +OUT: + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + StopTicks(); } void LegicRfWriter(int bytes, int offset) { - int byte_index=0, addr_sz=0; - uint8_t *BigBuf = BigBuf_get_addr(); + uint8_t *BigBuf = BigBuf_get_addr(); - LegicCommonInit(); - - DbpString("setting up legic card"); - uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); - switch_off_tag_rwd(); - switch(tag_type) { - case 0x1d: - if(offset+bytes > 0x100) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 256", offset+bytes); - return; - } - addr_sz = 8; - Dbprintf("MIM 256 card found, writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); - break; - case 0x3d: - if(offset+bytes > 0x400) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 1024", offset+bytes); - return; - } - addr_sz = 10; - Dbprintf("MIM 1024 card found, writing 0x%03.3x - 0x%03.3x ...", offset, offset+bytes); - break; - default: - Dbprintf("No or unknown card found, aborting"); - return; - } + // configure ARM and FPGA + init_reader(false); - LED_B_ON(); - perform_setup_phase_rwd(SESSION_IV); - legic_prng_forward(2); - while(byte_index < bytes) { - int r = legic_write_byte(BigBuf[byte_index+offset], byte_index+offset, addr_sz); - if((r != 0) || BUTTON_PRESS()) { - Dbprintf("operation aborted @ 0x%03.3x", byte_index); - switch_off_tag_rwd(); - LED_B_OFF(); - LED_C_OFF(); - return; - } - WDT_HIT(); - byte_index++; - if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); - } - LED_B_OFF(); - LED_C_OFF(); - DbpString("write successful"); -} - -int timestamp; - -/* Handle (whether to respond) a frame in tag mode */ -static void frame_handle_tag(struct legic_frame const * const f) -{ - uint8_t *BigBuf = BigBuf_get_addr(); - - /* First Part of Handshake (IV) */ - if(f->bits == 7) { - if(f->data == SESSION_IV) { - LED_C_ON(); - prng_timer->TC_CCR = AT91C_TC_SWTRG; - legic_prng_init(f->data); - frame_send_tag(0x3d, 6, 1); /* 0x3d^0x26 = 0x1b */ - legic_state = STATE_IV; - legic_read_count = 0; - legic_prng_bc = 0; - legic_prng_iv = f->data; - - /* TIMEOUT */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1); - while(timer->TC_CV < 280); - return; - } else if((prng_timer->TC_CV % 50) > 40) { - legic_prng_init(f->data); - frame_send_tag(0x3d, 6, 1); - SpinDelay(20); - return; - } - } - - /* 0x19==??? */ - if(legic_state == STATE_IV) { - if((f->bits == 6) && (f->data == (0x19 ^ get_key_stream(1, 6)))) { - legic_state = STATE_CON; - - /* TIMEOUT */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1); - while(timer->TC_CV < 200); - return; - } else { - legic_state = STATE_DISCON; - LED_C_OFF(); - Dbprintf("0x19 - Frame: %03.3x", f->data); - return; - } - } - - /* Read */ - if(f->bits == 11) { - if(legic_state == STATE_CON) { - int key = get_key_stream(-1, 11); //legic_phase_drift, 11); - int addr = f->data ^ key; addr = addr >> 1; - int data = BigBuf[addr]; - int hash = LegicCRC(addr, data, 11) << 8; - BigBuf[OFFSET_LOG+legic_read_count] = (uint8_t)addr; - legic_read_count++; - - //Dbprintf("Data:%03.3x, key:%03.3x, addr: %03.3x, read_c:%u", f->data, key, addr, read_c); - legic_prng_forward(legic_reqresp_drift); - - frame_send_tag(hash | data, 12, 1); - - /* SHORT TIMEOUT */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1); - legic_prng_forward(legic_frame_drift); - while(timer->TC_CV < 180); - return; - } - } - - /* Write */ - if(f->bits == 23) { - int key = get_key_stream(-1, 23); //legic_frame_drift, 23); - int addr = f->data ^ key; addr = addr >> 1; addr = addr & 0x3ff; - int data = f->data ^ key; data = data >> 11; data = data & 0xff; - - /* write command */ - legic_state = STATE_DISCON; - LED_C_OFF(); - Dbprintf("write - addr: %x, data: %x", addr, data); - return; - } - - if(legic_state != STATE_DISCON) { - Dbprintf("Unexpected: sz:%u, Data:%03.3x, State:%u, Count:%u", f->bits, f->data, legic_state, legic_read_count); - int i; - Dbprintf("IV: %03.3x", legic_prng_iv); - for(i = 0; i card.cardsize) { + bytes = card.cardsize - offset; + } + + // write in reverse order, only then is DCF (decremental field) writable + while(bytes-- > 0 && !BUTTON_PRESS()) { + if(!write_byte(bytes + offset, BigBuf[bytes + offset], card.addrsize)) { + Dbprintf("operation failed @ 0x%03.3x", bytes); + goto OUT; + } + } + + // OK + DbpString("Write successful"); + +OUT: + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + StopTicks(); } - -void LegicRfSimulate(int phase, int frame, int reqresp) -{ - /* ADC path high-frequency peak detector, FPGA in high-frequency simulator mode, - * modulation mode set to 212kHz subcarrier. We are getting the incoming raw - * envelope waveform on DIN and should send our response on DOUT. - * - * The LEGIC RF protocol is pulse-pause-encoding from reader to card, so we'll - * measure the time between two rising edges on DIN, and no encoding on the - * subcarrier from card to reader, so we'll just shift out our verbatim data - * on DOUT, 1 bit is 100us. The time from reader to card frame is still unclear, - * seems to be 300us-ish. - */ - - if(phase < 0) { - int i; - for(i=0; i<=reqresp; i++) { - legic_prng_init(SESSION_IV); - Dbprintf("i=%u, key 0x%3.3x", i, get_key_stream(i, frame)); - } - return; - } - - legic_phase_drift = phase; - legic_frame_drift = frame; - legic_reqresp_drift = reqresp; - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); - - /* Bitbang the receiver */ - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; - - setup_timer(); - crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); - - int old_level = 0; - int active = 0; - legic_state = STATE_DISCON; - - LED_B_ON(); - DbpString("Starting Legic emulator, press button to end"); - while(!BUTTON_PRESS()) { - int level = !!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - int time = timer->TC_CV; - - if(level != old_level) { - if(level == 1) { - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - if(FUZZ_EQUAL(time, RWD_TIME_1, RWD_TIME_FUZZ)) { - /* 1 bit */ - emit(1); - active = 1; - LED_A_ON(); - } else if(FUZZ_EQUAL(time, RWD_TIME_0, RWD_TIME_FUZZ)) { - /* 0 bit */ - emit(0); - active = 1; - LED_A_ON(); - } else if(active) { - /* invalid */ - emit(-1); - active = 0; - LED_A_OFF(); - } - } - } - - if(time >= (RWD_TIME_1+RWD_TIME_FUZZ) && active) { - /* Frame end */ - emit(-1); - active = 0; - LED_A_OFF(); - } - - if(time >= (20*RWD_TIME_1) && (timer->TC_SR & AT91C_TC_CLKSTA)) { - timer->TC_CCR = AT91C_TC_CLKDIS; - } - - old_level = level; - WDT_HIT(); - } - DbpString("Stopped"); - LED_B_OFF(); - LED_A_OFF(); - LED_C_OFF(); -} - diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 57ab7e6d..9f8cf457 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz +// 2018 AntiCat // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -11,8 +12,7 @@ #ifndef __LEGICRF_H #define __LEGICRF_H -extern void LegicRfSimulate(int phase, int frame, int reqresp); -extern int LegicRfReader(int bytes, int offset); +extern void LegicRfReader(int bytes, int offset); extern void LegicRfWriter(int bytes, int offset); #endif /* __LEGICRF_H */ diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c new file mode 100644 index 00000000..a149e0f9 --- /dev/null +++ b/armsrc/legicrfsim.c @@ -0,0 +1,469 @@ +//----------------------------------------------------------------------------- +// (c) 2009 Henryk Plötz +// 2016 Iceman +// 2018 AntiCat +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// LEGIC RF simulation code +//----------------------------------------------------------------------------- + +#include "proxmark3.h" +#include "apps.h" +#include "util.h" +#include "string.h" + +#include "legicrfsim.h" +#include "legic_prng.h" +#include "legic.h" +#include "crc.h" +#include "usb_cdc.h" // for usb_poll_validate_length + +static uint8_t* legic_mem; /* card memory, used for sim */ +static legic_card_select_t card;/* metadata of currently selected card */ +static crc_t legic_crc; + +//----------------------------------------------------------------------------- +// Frame timing and pseudorandom number generator +// +// The Prng is forwarded every 99.1us (TAG_BIT_PERIOD), except when the reader is +// transmitting. In that case the prng has to be forwarded every bit transmitted: +// - 31.3us for a 0 (RWD_TIME_0) +// - 99.1us for a 1 (RWD_TIME_1) +// +// The data dependent timing makes writing comprehensible code significantly +// harder. The current aproach forwards the prng data based if there is data on +// air and time based, using GetCountSspClk(), during computational and wait +// periodes. SSP Clock is clocked by the FPGA at 212 kHz (subcarrier frequency). +// +// To not have the necessity to calculate/guess exection time dependend timeouts +// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots. +//----------------------------------------------------------------------------- + +static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ + +#define TAG_FRAME_WAIT 70 /* 330us from READER frame end to TAG frame start */ +#define TAG_ACK_WAIT 758 /* 3.57ms from READER frame end to TAG write ACK */ +#define TAG_BIT_PERIOD 21 /* 99.1us */ + +#define RWD_TIME_PAUSE 4 /* 18.9us */ +#define RWD_TIME_1 21 /* RWD_TIME_PAUSE 18.9us off + 80.2us on = 99.1us */ +#define RWD_TIME_0 13 /* RWD_TIME_PAUSE 18.9us off + 42.4us on = 61.3us */ +#define RWD_CMD_TIMEOUT 40 /* 40 * 99.1us (arbitrary value) */ +#define RWD_MIN_FRAME_LEN 6 /* Shortest frame is 6 bits */ +#define RWD_MAX_FRAME_LEN 23 /* Longest frame is 23 bits */ + +#define RWD_PULSE 1 /* Pulse is signaled with GPIO_SSC_DIN high */ +#define RWD_PAUSE 0 /* Pause is signaled with GPIO_SSC_DIN low */ + +//----------------------------------------------------------------------------- +// Demodulation +//----------------------------------------------------------------------------- + +// Returns true if a pulse/pause is received within timeout +static inline bool wait_for(bool value, const uint32_t timeout) { + while((bool)(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN) != value) { + if(GetCountSspClk() > timeout) { + return false; + } + } + return true; +} + +// Returns a demedulated bit or -1 on code violation +// +// rx_bit decodes bits using a thresholds. rx_bit has to be called by as soon as +// a frame starts (first pause is received). rx_bit checks for a pause up to +// 18.9us followed by a pulse of 80.2us or 42.4us: +// - A bit length <18.9us is a code violation +// - A bit length >80.2us is a 1 +// - A bit length <80.2us is a 0 +// - A bit length >148.6us is a code violation +static inline int8_t rx_bit() { + // backup ts for threshold calculation + uint32_t bit_start = last_frame_end; + + // wait for pause to end + if(!wait_for(RWD_PULSE, bit_start + RWD_TIME_1*3/2)) { + return -1; + } + + // wait for next pause + if(!wait_for(RWD_PAUSE, bit_start + RWD_TIME_1*3/2)) { + return -1; + } + + // update bit and frame end + last_frame_end = GetCountSspClk(); + + // check for code violation (bit to short) + if(last_frame_end - bit_start < RWD_TIME_PAUSE) { + return -1; + } + + // apply threshold (average of RWD_TIME_0 and ) + return (last_frame_end - bit_start > (RWD_TIME_0 + RWD_TIME_1) / 2); +} + +//----------------------------------------------------------------------------- +// Modulation +// +// LEGIC RF uses a very basic load modulation from card to reader: +// - Subcarrier on for a 1 +// - Subcarrier off for for a 0 +// +// The 212kHz subcarrier is generated by the FPGA as well as a mathcing ssp clk. +// Each bit is transfered in a 99.1us slot and the first timeslot starts 330us +// after the final 20us pause generated by the reader. +//----------------------------------------------------------------------------- + +// Transmits a bit +// +// Note: The Subcarrier is not disabled during bits to prevent glitches. This is +// not mandatory but results in a cleaner signal. tx_frame will disable +// the subcarrier when the frame is done. +static inline void tx_bit(bool bit) { + LED_C_ON(); + + if(bit) { + // modulate subcarrier + HIGH(GPIO_SSC_DOUT); + } else { + // do not modulate subcarrier + LOW(GPIO_SSC_DOUT); + } + + // wait for tx timeslot to end + last_frame_end += TAG_BIT_PERIOD; + while(GetCountSspClk() < last_frame_end) { }; + LED_C_OFF(); +} + +//----------------------------------------------------------------------------- +// Frame Handling +// +// The LEGIC RF protocol from reader to card does not include explicit frame +// start/stop information or length information. The tag detects end of frame +// trough an extended pulse (>99.1us) without a pause. +// In reverse direction (card to reader) the number of bites is well known +// and depends only the command received (IV, ACK, READ or WRITE). +//----------------------------------------------------------------------------- + +static void tx_frame(uint32_t frame, uint8_t len) { + // wait for next tx timeslot + last_frame_end += TAG_FRAME_WAIT; + legic_prng_forward(TAG_FRAME_WAIT/TAG_BIT_PERIOD - 1); + while(GetCountSspClk() < last_frame_end) { }; + + // transmit frame, MSB first + for(uint8_t i = 0; i < len; ++i) { + bool bit = (frame >> i) & 0x01; + tx_bit(bit ^ legic_prng_get_bit()); + legic_prng_forward(1); + }; + + // disable subcarrier + LOW(GPIO_SSC_DOUT); +} + +static void tx_ack() { + // wait for ack timeslot + last_frame_end += TAG_ACK_WAIT; + legic_prng_forward(TAG_ACK_WAIT/TAG_BIT_PERIOD - 1); + while(GetCountSspClk() < last_frame_end) { }; + + // transmit ack (ack is not encrypted) + tx_bit(true); + legic_prng_forward(1); + + // disable subcarrier + LOW(GPIO_SSC_DOUT); +} + +// Returns a demedulated frame or -1 on code violation +// +// Since TX to RX delay is arbitrary rx_frame has to: +// - detect start of frame (first pause) +// - forward prng based on ts/TAG_BIT_PERIOD +// - receive the frame +// - detect end of frame (last pause) +static int32_t rx_frame(uint8_t *len) { + int32_t frame = 0; + + // add 2 SSP clock cycles (1 for tx and 1 for rx pipeline delay) + // those will be substracted at the end of the rx phase + last_frame_end -= 2; + + // wait for first pause (start of frame) + for(uint8_t i = 0; true; ++i) { + // increment prng every TAG_BIT_PERIOD + last_frame_end += TAG_BIT_PERIOD; + legic_prng_forward(1); + + // if start of frame was received exit delay loop + if(wait_for(RWD_PAUSE, last_frame_end)) { + last_frame_end = GetCountSspClk(); + break; + } + + // check for code violation + if(i > RWD_CMD_TIMEOUT) { + return -1; + } + } + + // receive frame + for(*len = 0; true; ++(*len)) { + // receive next bit + LED_D_ON(); + int8_t bit = rx_bit(); + LED_D_OFF(); + + // check for code violation and to short / long frame + if((bit < 0) && ((*len < RWD_MIN_FRAME_LEN) || (*len > RWD_MAX_FRAME_LEN))) { + return -1; + } + + // check for code violation caused by end of frame + if(bit < 0) { + break; + } + + // append bit + frame |= (bit ^ legic_prng_get_bit()) << (*len); + legic_prng_forward(1); + } + + // rx_bit sets coordination timestamp to start of pause, append pause duration + // and substract 2 SSP clock cycles (1 for rx and 1 for tx pipeline delay) to + // obtain exact end of frame. + last_frame_end += RWD_TIME_PAUSE - 2; + + return frame; +} + +//----------------------------------------------------------------------------- +// Legic Simulator +//----------------------------------------------------------------------------- + +static int32_t init_card(uint8_t cardtype, legic_card_select_t *p_card) { + p_card->tagtype = cardtype; + + switch(p_card->tagtype) { + case 0: + p_card->cmdsize = 6; + p_card->addrsize = 5; + p_card->cardsize = 22; + break; + case 1: + p_card->cmdsize = 9; + p_card->addrsize = 8; + p_card->cardsize = 256; + break; + case 2: + p_card->cmdsize = 11; + p_card->addrsize = 10; + p_card->cardsize = 1024; + break; + default: + p_card->cmdsize = 0; + p_card->addrsize = 0; + p_card->cardsize = 0; + return 2; + } + return 0; +} + +static void init_tag() { + // configure FPGA + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR + | FPGA_HF_SIMULATOR_MODULATE_212K); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // configure SSC with defaults + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); + + // first pull output to low to prevent glitches then re-claim GPIO_SSC_DOUT + LOW(GPIO_SSC_DOUT); + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + + // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. + legic_mem = BigBuf_get_addr(); + + // init crc calculator + crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0); + + // start 212kHz timer (running from SSP Clock) + StartCountSspClk(); +} + +// Setup reader to card connection +// +// The setup consists of a three way handshake: +// - Receive initialisation vector 7 bits +// - Transmit card type 6 bits +// - Receive Acknowledge 6 bits +static int32_t setup_phase(legic_card_select_t *p_card) { + uint8_t len = 0; + + // init coordination timestamp + last_frame_end = GetCountSspClk(); + + // reset prng + legic_prng_init(0); + + // wait for iv + int32_t iv = rx_frame(&len); + if((len != 7) || (iv < 0)) { + return -1; + } + + // configure prng + legic_prng_init(iv); + + // reply with card type + switch(p_card->tagtype) { + case 0: + tx_frame(0x0D, 6); + break; + case 1: + tx_frame(0x1D, 6); + break; + case 2: + tx_frame(0x3D, 6); + break; + } + + // wait for ack + int32_t ack = rx_frame(&len); + if((len != 6) || (ack < 0)) { + return -1; + } + + // validate data + switch(p_card->tagtype) { + case 0: + if(ack != 0x19) return -1; + break; + case 1: + if(ack != 0x39) return -1; + break; + case 2: + if(ack != 0x39) return -1; + break; + } + + // During rx the prng is clocked using the variable reader period. + // Since rx_frame detects end of frame by detecting a code violation, + // the prng is off by one bit period after each rx phase. Hence, tx + // code advances the prng by (TAG_FRAME_WAIT/TAG_BIT_PERIOD - 1). + // This is not possible for back to back rx, so this quirk reduces + // the gap by one period. + last_frame_end += TAG_BIT_PERIOD; + + return 0; +} + +static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) { + crc_clear(&legic_crc); + crc_update(&legic_crc, (value << cmd_sz) | cmd, 8 + cmd_sz); + return crc_finish(&legic_crc); +} + +static int32_t connected_phase(legic_card_select_t *p_card) { + uint8_t len = 0; + + // wait for command + int32_t cmd = rx_frame(&len); + if(cmd < 0) { + return -1; + } + + // check if command is LEGIC_READ + if(len == p_card->cmdsize) { + // prepare data + uint8_t byte = legic_mem[cmd >> 1]; + uint8_t crc = calc_crc4(cmd, p_card->cmdsize, byte); + + // transmit data + tx_frame((crc << 8) | byte, 12); + + return 0; + } + + // check if command is LEGIC_WRITE + if(len == p_card->cmdsize + 8 + 4) { + // decode data + uint16_t mask = (1 << p_card->addrsize) - 1; + uint16_t addr = (cmd >> 1) & mask; + uint8_t byte = (cmd >> p_card->cmdsize) & 0xff; + uint8_t crc = (cmd >> (p_card->cmdsize + 8)) & 0xf; + + // check received against calculated crc + uint8_t calc_crc = calc_crc4(addr << 1, p_card->cmdsize, byte); + if(calc_crc != crc) { + Dbprintf("!!! crc mismatch: %x != %x !!!", calc_crc, crc); + return -1; + } + + // store data + legic_mem[addr] = byte; + + // transmit ack + tx_ack(); + + return 0; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Command Line Interface +// +// Only this function is public / called from appmain.c +//----------------------------------------------------------------------------- + +void LegicRfSimulate(uint8_t cardtype) { + // configure ARM and FPGA + init_tag(); + + // verify command line input + if(init_card(cardtype, &card) != 0) { + DbpString("Unknown tagtype."); + goto OUT; + } + + LED_A_ON(); + DbpString("Starting Legic emulator, press button to end"); + while(!BUTTON_PRESS() && !usb_poll_validate_length()) { + WDT_HIT(); + + // wait for carrier, restart after timeout + if(!wait_for(RWD_PULSE, GetCountSspClk() + TAG_BIT_PERIOD)) { + continue; + } + + // wait for connection, restart on error + if(setup_phase(&card)) { + continue; + } + + // conection is established, process commands until one fails + while(!connected_phase(&card)) { + WDT_HIT(); + } + } + +OUT: + DbpString("Stopped"); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_A_OFF(); + LED_C_OFF(); + LED_D_OFF(); + StopTicks(); +} diff --git a/armsrc/legicrfsim.h b/armsrc/legicrfsim.h new file mode 100644 index 00000000..c1c8a86e --- /dev/null +++ b/armsrc/legicrfsim.h @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// (c) 2009 Henryk Plötz +// 2018 AntiCat +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// LEGIC RF emulation public interface +//----------------------------------------------------------------------------- + +#ifndef __LEGICRFSIM_H +#define __LEGICRFSIM_H + +#include "proxmark3.h" + +extern void LegicRfSimulate(uint8_t tagtype); + +#endif /* __LEGICRFSIM_H */ diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 641c02e8..b56c3f51 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -4,7 +4,7 @@ // the license. //----------------------------------------------------------------------------- // Miscellaneous routines for low frequency tag operations. -// Tags supported here so far are Texas Instruments (TI), HID +// Tags supported here so far are Texas Instruments (TI), HID, EM4x05, EM410x // Also routines for raw mode reading/simulating of LF waveform //----------------------------------------------------------------------------- @@ -28,51 +28,103 @@ */ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint32_t period_1, uint8_t *command) { + // start timer + StartTicks(); - int divisor_used = 95; // 125 KHz - // see if 'h' was specified + // use lf config settings + sample_config *sc = getSamplingConfig(); - if (command[strlen((char *) command) - 1] == 'h') - divisor_used = 88; // 134.8 KHz - - sample_config sc = { 0,0,1, divisor_used, 0}; - setSamplingConfig(&sc); - //clear read buffer - BigBuf_Clear_keep_EM(); - - /* Make sure the tag is reset */ + // Make sure the tag is reset FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(2500); + WaitMS(2500); - LFSetupFPGAForADC(sc.divisor, 1); + // clear read buffer (after fpga bitstream loaded...) + BigBuf_Clear_keep_EM(); + + // power on + LFSetupFPGAForADC(sc->divisor, 1); // And a little more time for the tag to fully power up - SpinDelay(2000); - + WaitMS(2000); + // if delay_off = 0 then just bitbang 1 = antenna on 0 = off for respective periods. + bool bitbang = delay_off == 0; // now modulate the reader field - while(*command != '\0' && *command != ' ') { + + if (bitbang) { + // HACK it appears the loop and if statements take up about 7us so adjust waits accordingly... + uint8_t hack_cnt = 7; + if (period_0 < hack_cnt || period_1 < hack_cnt) { + DbpString("Warning periods cannot be less than 7us in bit bang mode"); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + return; + } + + // hack2 needed--- it appears to take about 8-16us to turn the antenna back on + // leading to ~ 1 to 2 125khz samples extra in every off period + // so we should test for last 0 before next 1 and reduce period_0 by this extra amount... + // but is this time different for every antenna or other hw builds??? more testing needed + + // prime cmd_len to save time comparing strings while modulating + int cmd_len = 0; + while(command[cmd_len] != '\0' && command[cmd_len] != ' ') + cmd_len++; + + int counter = 0; + bool off = false; + for (counter = 0; counter < cmd_len; counter++) { + // if cmd = 0 then turn field off + if (command[counter] == '0') { + // if field already off leave alone (affects timing otherwise) + if (off == false) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + off = true; + } + // note we appear to take about 7us to switch over (or run the if statements/loop...) + WaitUS(period_0-hack_cnt); + // else if cmd = 1 then turn field on + } else { + // if field already on leave alone (affects timing otherwise) + if (off) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + LED_D_ON(); + off = false; + } + // note we appear to take about 7us to switch over (or run the if statements/loop...) + WaitUS(period_1-hack_cnt); + } + } + } else { // old mode of cmd read using delay as off period + while(*command != '\0' && *command != ' ') { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + WaitUS(delay_off); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + LED_D_ON(); + if(*(command++) == '0') { + WaitUS(period_0); + } else { + WaitUS(period_1); + } + } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); - SpinDelayUs(delay_off); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc.divisor); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - LED_D_ON(); - if(*(command++) == '0') - SpinDelayUs(period_0); - else - SpinDelayUs(period_1); + WaitUS(delay_off); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); } - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); - SpinDelayUs(delay_off); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc.divisor); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); // now do the read DoAcquisition_config(false, 0); + + // Turn off antenna + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // tell client we are done + cmd_send(CMD_ACK,0,0,0,0,0); } /* blank r/w tag data stream @@ -516,7 +568,7 @@ static void fcAll(uint8_t fc, int *n, uint8_t clock, uint16_t *modCnt) uint8_t wavesPerClock = clock/fc; uint8_t mod = clock % fc; //modifier uint8_t modAdj = fc/mod; //how often to apply modifier - bool modAdjOk = !(fc % mod); //if (fc % mod==0) modAdjOk=TRUE; + bool modAdjOk = !(fc % mod); //if (fc % mod==0) modAdjOk=true; // loop through clock - step field clock for (uint8_t idx=0; idx < wavesPerClock; idx++){ // put 1/2 FC length 1's and 1/2 0's per field clock wave (to create the wave) @@ -541,7 +593,7 @@ static void fcAll(uint8_t fc, int *n, uint8_t clock, uint16_t *modCnt) // prepare a waveform pattern in the buffer based on the ID given then // simulate a HID tag until the button is pressed -void CmdHIDsimTAG(int hi, int lo, int ledcontrol) +void CmdHIDsimTAG(int hi2, int hi, int lo, int ledcontrol) { int n=0, i=0; /* @@ -554,8 +606,8 @@ void CmdHIDsimTAG(int hi, int lo, int ledcontrol) nor 1 bits, they are special patterns (a = set of 12 fc8 and b = set of 10 fc10) */ - if (hi>0xFFF) { - DbpString("Tags can only have 44 bits. - USE lf simfsk for larger tags"); + if (hi2>0x0FFFFFFF) { + DbpString("Tags can only have 44 or 84 bits. - USE lf simfsk for larger tags"); return; } // set LF so we don't kill the bigbuf we are setting with simulation data. @@ -569,13 +621,35 @@ void CmdHIDsimTAG(int hi, int lo, int ledcontrol) fc(8, &n); fc(10, &n); // logical 0 WDT_HIT(); - // manchester encode bits 43 to 32 - for (i=11; i>=0; i--) { - if ((i%4)==3) fc(0,&n); - if ((hi>>i)&1) { - fc(10, &n); fc(8, &n); // low-high transition - } else { - fc(8, &n); fc(10, &n); // high-low transition + if (hi2 > 0 || hi > 0xFFF){ + // manchester encode bits 91 to 64 (91-84 are part of the header) + for (i=27; i>=0; i--) { + if ((i%4)==3) fc(0,&n); + if ((hi2>>i)&1) { + fc(10, &n); fc(8, &n); // low-high transition + } else { + fc(8, &n); fc(10, &n); // high-low transition + } + } + WDT_HIT(); + // manchester encode bits 63 to 32 + for (i=31; i>=0; i--) { + if ((i%4)==3) fc(0,&n); + if ((hi>>i)&1) { + fc(10, &n); fc(8, &n); // low-high transition + } else { + fc(8, &n); fc(10, &n); // high-low transition + } + } + } else { + // manchester encode bits 43 to 32 + for (i=11; i>=0; i--) { + if ((i%4)==3) fc(0,&n); + if ((hi>>i)&1) { + fc(10, &n); fc(8, &n); // low-high transition + } else { + fc(8, &n); fc(10, &n); // high-low transition + } } } @@ -768,9 +842,9 @@ void CmdPSKsimTag(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream) for (i=0; i0 && lo>0 && (size==96 || size==192)){ + uint8_t bitlen = 0; + uint32_t fc = 0; + uint32_t cardnum = 0; + bool decoded = false; + // go over previously decoded manchester data and decode into usable tag ID - if (hi2 != 0){ //extra large HID tags 88/192 bits - Dbprintf("TAG ID: %x%08x%08x (%d)", - (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - }else { //standard HID tags 44/96 bits - //Dbprintf("TAG ID: %x%08x (%d)",(unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); //old print cmd - uint8_t bitlen = 0; - uint32_t fc = 0; - uint32_t cardnum = 0; - if (((hi>>5)&1) == 1){//if bit 38 is set then < 37 bit format is used - uint32_t lo2=0; - lo2=(((hi & 31) << 12) | (lo>>20)); //get bits 21-37 to check for format len bit - uint8_t idx3 = 1; - while(lo2 > 1){ //find last bit set to 1 (format len bit) - lo2=lo2 >> 1; - idx3++; - } - bitlen = idx3+19; - fc =0; - cardnum=0; - if(bitlen == 26){ - cardnum = (lo>>1)&0xFFFF; - fc = (lo>>17)&0xFF; - } - if(bitlen == 37){ - cardnum = (lo>>1)&0x7FFFF; - fc = ((hi&0xF)<<12)|(lo>>20); - } - if(bitlen == 34){ - cardnum = (lo>>1)&0xFFFF; - fc= ((hi&1)<<15)|(lo>>17); - } - if(bitlen == 35){ - cardnum = (lo>>1)&0xFFFFF; - fc = ((hi&1)<<11)|(lo>>21); - } + if ((hi2 & 0x000FFFF) != 0){ //extra large HID tags 88/192 bits + uint32_t bp = hi2 & 0x000FFFFF; + bitlen = 63; + while (bp > 0) { + bp = bp >> 1; + bitlen++; } - else { //if bit 38 is not set then 37 bit format is used - bitlen= 37; - fc =0; - cardnum=0; - if(bitlen==37){ - cardnum = (lo>>1)&0x7FFFF; - fc = ((hi&0xF)<<12)|(lo>>20); - } + } else if ((hi >> 6) > 0) { + uint32_t bp = hi; + bitlen = 31; + while (bp > 0) { + bp = bp >> 1; + bitlen++; + } + } else if (((hi >> 5) & 1) == 0) { + bitlen = 37; + } else if ((hi & 0x0000001F) > 0 ) { + uint32_t bp = (hi & 0x0000001F); + bitlen = 31; + while (bp > 0) { + bp = bp >> 1; + bitlen++; + } + } else { + uint32_t bp = lo; + bitlen = 0; + while (bp > 0) { + bp = bp >> 1; + bitlen++; } - //Dbprintf("TAG ID: %x%08x (%d)", - // (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - Dbprintf("TAG ID: %x%08x (%d) - Format Len: %dbit - FC: %d - Card: %d", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - (unsigned int) bitlen, (unsigned int) fc, (unsigned int) cardnum); } + switch (bitlen){ + case 26: + cardnum = (lo>>1)&0xFFFF; + fc = (lo>>17)&0xFF; + decoded = true; + break; + case 35: + cardnum = (lo>>1)&0xFFFFF; + fc = ((hi&1)<<11)|(lo>>21); + decoded = true; + break; + } + + if (hi2 != 0) //extra large HID tags 88/192 bits + Dbprintf("TAG ID: %x%08x%08x (%d)", + (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); + else + Dbprintf("TAG ID: %x%08x (%d)", + (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); + + if (decoded) + Dbprintf("Format Len: %dbits - FC: %d - Card: %d", + (unsigned int) bitlen, (unsigned int) fc, (unsigned int) cardnum); + if (findone){ if (ledcontrol) LED_A_OFF(); + *high2 = hi2; *high = hi; *low = lo; break; @@ -1159,7 +1242,7 @@ void T55xxResetRead(void) { TurnReadLFOn(READ_GAP); // Acquisition - DoPartialAcquisition(0, true, BigBuf_max_traceLen()); + DoPartialAcquisition(0, true, BigBuf_max_traceLen(), 0); // Turn the field off FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off @@ -1291,7 +1374,7 @@ void T55xxReadBlock(uint16_t arg0, uint8_t Block, uint32_t Pwd) { // Acquisition // Now do the acquisition - DoPartialAcquisition(0, true, 12000); + DoPartialAcquisition(0, true, 12000, 0); // Turn the field off FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off @@ -1690,7 +1773,7 @@ void EM4xReadWord(uint8_t Address, uint32_t Pwd, uint8_t PwdMode) { SendForward(fwd_bit_count); WaitUS(400); // Now do the acquisition - DoPartialAcquisition(20, true, 6000); + DoPartialAcquisition(20, true, 6000, 1000); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off LED_A_OFF(); @@ -1723,7 +1806,7 @@ void EM4xWriteWord(uint32_t flag, uint32_t Data, uint32_t Pwd) { WaitUS(6500); //Capture response if one exists - DoPartialAcquisition(20, true, 6000); + DoPartialAcquisition(20, true, 6000, 1000); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off LED_A_OFF(); @@ -1766,7 +1849,7 @@ void Cotag(uint32_t arg0) { SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_ADC); // start clock - 1.5ticks is 1us StartTicks(); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 5d1f9248..ffbc0da8 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -101,7 +101,7 @@ void LFSetupFPGAForADC(int divisor, bool lf_field) // Give it a bit of time for the resonant antenna to settle. SpinDelay(50); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_ADC); } /** @@ -119,7 +119,7 @@ void LFSetupFPGAForADC(int divisor, bool lf_field) * @param silent - is true, now outputs are made. If false, dbprints the status * @return the number of bits occupied by the samples. */ -uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averaging, int trigger_threshold, bool silent, int bufsize) +uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averaging, int trigger_threshold, bool silent, int bufsize, int cancel_after) { //. uint8_t *dest = BigBuf_get_addr(); @@ -140,6 +140,7 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag uint32_t sample_sum =0 ; uint32_t sample_total_numbers =0 ; uint32_t sample_total_saved =0 ; + uint32_t cancel_counter = 0; while(!BUTTON_PRESS() && !usb_poll_validate_length() ) { WDT_HIT(); @@ -151,9 +152,13 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; LED_D_OFF(); // threshold either high or low values 128 = center 0. if trigger = 178 - if ((trigger_threshold > 0) && (sample < (trigger_threshold+128)) && (sample > (128-trigger_threshold))) // + if ((trigger_threshold > 0) && (sample < (trigger_threshold+128)) && (sample > (128-trigger_threshold))) { // + if (cancel_after > 0) { + cancel_counter++; + if (cancel_after == cancel_counter) break; + } continue; - + } trigger_threshold = 0; sample_total_numbers++; @@ -213,7 +218,7 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag */ uint32_t DoAcquisition_default(int trigger_threshold, bool silent) { - return DoAcquisition(1,8,0,trigger_threshold,silent,0); + return DoAcquisition(1,8,0,trigger_threshold,silent,0,0); } uint32_t DoAcquisition_config(bool silent, int sample_size) { @@ -222,11 +227,12 @@ uint32_t DoAcquisition_config(bool silent, int sample_size) ,config.averaging ,config.trigger_threshold ,silent - ,sample_size); + ,sample_size + ,0); } -uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size) { - return DoAcquisition(1,8,0,trigger_threshold,silent,sample_size); +uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size, int cancel_after) { + return DoAcquisition(1,8,0,trigger_threshold,silent,sample_size,cancel_after); } uint32_t ReadLF(bool activeField, bool silent, int sample_size) @@ -260,7 +266,7 @@ uint32_t SnoopLF() } /** -* acquisition of Cotag LF signal. Similart to other LF, since the Cotag has such long datarate RF/384 +* acquisition of Cotag LF signal. Similar to other LF, since the Cotag has such long datarate RF/384 * and is Manchester?, we directly gather the manchester data into bigbuff **/ #define COTAG_T1 384 @@ -329,8 +335,8 @@ uint32_t doCotagAcquisitionManchester() { uint8_t sample = 0, firsthigh = 0, firstlow = 0; uint16_t sample_counter = 0, period = 0; uint8_t curr = 0, prev = 0; - - while (!BUTTON_PRESS() && !usb_poll_validate_length() && (sample_counter < bufsize) ) { + uint16_t noise_counter = 0; + while (!BUTTON_PRESS() && !usb_poll_validate_length() && (sample_counter < bufsize) && (noise_counter < (COTAG_T1<<1)) ) { WDT_HIT(); if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { AT91C_BASE_SSC->SSC_THR = 0x43; @@ -343,14 +349,20 @@ uint32_t doCotagAcquisitionManchester() { // find first peak if ( !firsthigh ) { - if (sample < COTAG_ONE_THRESHOLD) + if (sample < COTAG_ONE_THRESHOLD) { + noise_counter++; continue; + } + noise_counter = 0; firsthigh = 1; } if ( !firstlow ){ - if (sample > COTAG_ZERO_THRESHOLD ) + if (sample > COTAG_ZERO_THRESHOLD ) { + noise_counter++; continue; + } + noise_counter=0; firstlow = 1; } diff --git a/armsrc/lfsampling.h b/armsrc/lfsampling.h index 0531e0c9..ea044f3c 100644 --- a/armsrc/lfsampling.h +++ b/armsrc/lfsampling.h @@ -21,7 +21,7 @@ uint32_t SampleLF(bool silent, int sample_size); uint32_t SnoopLF(); // adds sample size to default options -uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size); +uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size, int cancel_after); /** * @brief Does sample acquisition, ignoring the config values set in the sample_config. diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 0e9c3672..ca513cec 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -20,9 +20,8 @@ #include "parity.h" #include "crc.h" -#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) -#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication - +#define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) +#define HARDNESTED_PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication // the block number for the ISO14443-4 PCB static uint8_t pcb_blocknum = 0; @@ -59,7 +58,7 @@ void MifareReadBlock(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)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); break; }; @@ -106,7 +105,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes){ clear_trace(); - if(!iso14443a_select_card(NULL, NULL, NULL, true, 0)) { + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; @@ -141,7 +140,7 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) clear_trace(); - int len = iso14443a_select_card(NULL, NULL, NULL, true, 0); + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); if(!len) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%02X)",len); OnError(1); @@ -217,7 +216,7 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) LED_C_OFF(); isOK = 1; - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { isOK = 0; if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); } @@ -281,7 +280,7 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) return; } - int len = iso14443a_select_card(NULL, NULL, NULL, true, 0); + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); if (!len) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%d)",len); OnError(1); @@ -383,7 +382,7 @@ void MifareWriteBlock(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)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); break; }; @@ -483,7 +482,7 @@ void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) clear_trace(); - if(!iso14443a_select_card(NULL, NULL, NULL, true, 0)) { + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); OnError(0); return; @@ -542,7 +541,7 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain){ clear_trace(); - if(!iso14443a_select_card(NULL, NULL, NULL, true, 0)) { + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); OnError(0); return; @@ -662,7 +661,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, if (!have_uid) { // 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)) { + if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); continue; } @@ -674,14 +673,14 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, } have_uid = true; } else { // no need for anticollision. We can directly select the card - if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels)) { + if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); continue; } } if (slow) { - timeout = GetCountSspClk() + PRE_AUTHENTICATION_LEADTIME; + timeout = GetCountSspClk() + HARDNESTED_PRE_AUTHENTICATION_LEADTIME; while(GetCountSspClk() < timeout); } @@ -698,11 +697,12 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, continue; } - // send a dummy byte as reader response in order to trigger the cards authentication timeout - uint8_t dummy_answer = 0; - ReaderTransmit(&dummy_answer, 1, NULL); - timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; + // send an incomplete dummy response in order to trigger the card's authentication failure timeout + uint8_t dummy_answer[1] = {0}; + ReaderTransmit(dummy_answer, 1, NULL); + timeout = GetCountSspClk() + HARDNESTED_AUTHENTICATION_TIMEOUT; + num_nonces++; if (num_nonces % 2) { memcpy(buf+i, receivedAnswer, 4); @@ -807,7 +807,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat continue; } - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card"); rtr--; continue; @@ -881,7 +881,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat continue; } - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card"); continue; }; @@ -961,24 +961,14 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat // MIFARE check keys. key count up to 85. // //----------------------------------------------------------------------------- -void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) +void MifareChkKeys(uint16_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) { uint8_t blockNo = arg0 & 0xff; uint8_t keyType = (arg0 >> 8) & 0xff; - bool clearTrace = arg1; + bool clearTrace = arg1 & 0x01; + bool multisectorCheck = arg1 & 0x02; + uint8_t set14aTimeout = (arg1 >> 8) & 0xff; uint8_t keyCount = arg2; - uint64_t ui64Key = 0; - - bool have_uid = false; - uint8_t cascade_levels = 0; - uint32_t timeout = 0; - int i; - byte_t isOK = 0; - uint8_t uid[10]; - uint32_t cuid; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; // clear debug level int OLD_MF_DBGLEVEL = MF_DBGLEVEL; @@ -992,52 +982,33 @@ void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) if (clearTrace) clear_trace(); set_tracing(true); - for (i = 0; i < keyCount; i++) { -// if(mifare_classic_halt(pcs, cuid)) { -// if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Halt error"); -// } - - // Iceman: use piwi's faster nonce collecting part in hardnested. - if (!have_uid) { // 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)) { - if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card"); - --i; // try same key once again - continue; - } - 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_select_card(uid, NULL, NULL, false, cascade_levels)) { - if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (UID)"); - --i; // try same key once again - continue; - } - } - - ui64Key = bytes_to_num(datain + i * 6, 6); - if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - uint8_t dummy_answer = 0; - ReaderTransmit(&dummy_answer, 1, NULL); - timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; - - // wait for the card to become ready again - while(GetCountSspClk() < timeout); - continue; - } - - isOK = 1; - break; + if (set14aTimeout){ + iso14a_set_timeout(set14aTimeout * 10); // timeout: ms = x/106 35-minimum, 50-OK 106-recommended 500-safe } + + if (multisectorCheck) { + TKeyIndex keyIndex = {{0}}; + uint8_t sectorCnt = blockNo; + int res = MifareMultisectorChk(datain, keyCount, sectorCnt, keyType, OLD_MF_DBGLEVEL, &keyIndex); - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,datain + i * 6,6); - LED_B_OFF(); + LED_B_ON(); + if (res >= 0) { + cmd_send(CMD_ACK, 1, 0, 0, keyIndex, 80); + } else { + cmd_send(CMD_ACK, 0, 0, 0, NULL, 0); + } + LED_B_OFF(); + } else { + int res = MifareChkBlockKeys(datain, keyCount, blockNo, keyType, OLD_MF_DBGLEVEL); + + LED_B_ON(); + if (res > 0) { + cmd_send(CMD_ACK, 1, 0, 0, datain + (res - 1) * 6, 6); + } else { + cmd_send(CMD_ACK, 0, 0, 0, NULL, 0); + } + LED_B_OFF(); + } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); @@ -1111,7 +1082,7 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai bool isOK = true; - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { isOK = false; if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); } @@ -1170,6 +1141,143 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai // Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) // //----------------------------------------------------------------------------- + +static bool isBlockTrailer(int blockN) { + if (blockN >= 0 && blockN < 128) { + return ((blockN & 0x03) == 0x03); + } + if (blockN >= 128 && blockN <= 256) { + return ((blockN & 0x0F) == 0x0F); + } + return false; +} + +void MifareCWipe(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ + // var + byte_t isOK = 0; + uint32_t numBlocks = arg0; + // cmdParams: + // bit 0 - wipe gen1a + // bit 1 - fill card with default data + // bit 2 - gen1a = 0, gen1b = 1 + uint8_t cmdParams = arg1; + bool needWipe = cmdParams & 0x01; + bool needFill = cmdParams & 0x02; + bool gen1b = cmdParams & 0x04; + + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; + + uint8_t block0[16] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xAF}; + uint8_t block1[16] = {0x00}; + uint8_t blockK[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x77, 0x8F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t d_block[18] = {0x00}; + + // card commands + uint8_t wupC1[] = { 0x40 }; + uint8_t wupC2[] = { 0x43 }; + uint8_t wipeC[] = { 0x41 }; + + // iso14443 setup + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + // tracing + clear_trace(); + set_tracing(true); + + while (true){ + // wipe + if (needWipe){ + ReaderTransmitBitsPar(wupC1,7,0, NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error"); + break; + }; + + ReaderTransmit(wipeC, sizeof(wipeC), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("wipeC error"); + break; + }; + + if(mifare_classic_halt(NULL, 0)) { + if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); + }; + }; + + // put default data + if (needFill){ + // select commands + ReaderTransmitBitsPar(wupC1, 7, 0, NULL); + + // gen1b magic tag : do no issue wupC2 and don't expect 0x0a response after SELECT_UID (after getting UID from chip in 'hf mf csetuid' command) + if (!gen1b) { + + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error"); + break; + }; + + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("wupC2 error"); + break; + }; + } + + // send blocks command + for (int blockNo = 0; blockNo < numBlocks; blockNo++) { + if ((mifare_sendcmd_short(NULL, 0, 0xA0, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("write block send command error"); + break; + }; + + // check type of block and add crc + if (!isBlockTrailer(blockNo)){ + memcpy(d_block, block1, 16); + } else { + memcpy(d_block, blockK, 16); + } + if (blockNo == 0) { + memcpy(d_block, block0, 16); + } + AppendCrc14443a(d_block, 16); + + // send write command + ReaderTransmit(d_block, sizeof(d_block), NULL); + if ((ReaderReceive(receivedAnswer, receivedAnswerPar) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("write block send data error"); + break; + }; + } + + // halt + // do no issue halt command for gen1b + if (!gen1b) { + if (mifare_classic_halt(NULL, 0)) { + if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); + break; + } + } + } + break; + } + + // send USB response + LED_B_ON(); + cmd_send(CMD_ACK,isOK,0,0,NULL,0); + LED_B_OFF(); + + // reset fpga + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + + return; +} + void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ // params @@ -1212,15 +1320,16 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai // get UID from chip if (workFlags & 0x01) { - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; + // Continue, if we set wrong UID or wrong UID checksum or some ATQA or SAK we will can't select card. But we need to write block 0 to make card work. + //break; }; if(mifare_classic_halt(NULL, cuid)) { if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); // Continue, some magic tags misbehavies and send an answer to it. - // break; + // break; }; }; @@ -1239,7 +1348,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai break; }; - if(mifare_classic_halt(NULL, cuid)) { + if(mifare_classic_halt(NULL, 0)) { if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); // Continue, some magic tags misbehavies and send an answer to it. // break; @@ -1283,7 +1392,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai if (workFlags & 0x04) { // do no issue halt command for gen1b magic tag (#db# halt error. response len: 1) if (!(workFlags & 0x40)) { - if (mifare_classic_halt(NULL, cuid)) { + if (mifare_classic_halt(NULL, 0)) { if (MF_DBGLEVEL > 2) Dbprintf("Halt error"); // Continue, some magic tags misbehavies and send an answer to it. // break; @@ -1405,6 +1514,14 @@ void MifareCIdent(){ uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); ReaderTransmitBitsPar(wupC1,7,0, NULL); if(ReaderReceive(receivedAnswer, receivedAnswerPar) && (receivedAnswer[0] == 0x0a)) { @@ -1418,8 +1535,13 @@ void MifareCIdent(){ // From iceman1001: removed the if, since some magic tags misbehavies and send an answer to it. mifare_classic_halt(NULL, 0); - + + LED_B_ON(); cmd_send(CMD_ACK,isOK,0,0,0,0); + LED_B_OFF(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } // @@ -1435,7 +1557,7 @@ void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain){ iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); - int len = iso14443a_select_card(uid, NULL, &cuid, true, 0); + int len = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); if(!len) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); OnError(1); diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 145e2989..e17fa998 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -16,7 +16,6 @@ #include "proxmark3.h" #include "apps.h" #include "util.h" -#include "string.h" #include "iso14443crc.h" #include "iso14443a.h" diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c new file mode 100644 index 00000000..c9264836 --- /dev/null +++ b/armsrc/mifaresim.c @@ -0,0 +1,779 @@ +//----------------------------------------------------------------------------- +// Merlok - June 2011, 2012 +// Gerhard de Koning Gans - May 2008 +// Hagen Fritsch - June 2010 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Mifare Classic Card Simulation +//----------------------------------------------------------------------------- + +#include "mifaresim.h" +#include "iso14443a.h" +#include "iso14443crc.h" +#include "crapto1/crapto1.h" +#include "BigBuf.h" +#include "string.h" +#include "mifareutil.h" +#include "fpgaloader.h" +#include "proxmark3.h" +#include "usb_cdc.h" +#include "cmd.h" +#include "protocols.h" +#include "apps.h" + +//mifare emulator states +#define MFEMUL_NOFIELD 0 +#define MFEMUL_IDLE 1 +#define MFEMUL_SELECT1 2 +#define MFEMUL_SELECT2 3 +#define MFEMUL_SELECT3 4 +#define MFEMUL_AUTH1 5 +#define MFEMUL_AUTH2 6 +#define MFEMUL_WORK 7 +#define MFEMUL_WRITEBL2 8 +#define MFEMUL_INTREG_INC 9 +#define MFEMUL_INTREG_DEC 10 +#define MFEMUL_INTREG_REST 11 +#define MFEMUL_HALTED 12 + +#define cardSTATE_TO_IDLE() { cardSTATE = MFEMUL_IDLE; LED_B_OFF(); LED_C_OFF(); } + +#define AC_DATA_READ 0 +#define AC_DATA_WRITE 1 +#define AC_DATA_INC 2 +#define AC_DATA_DEC_TRANS_REST 3 +#define AC_KEYA_READ 0 +#define AC_KEYA_WRITE 1 +#define AC_KEYB_READ 2 +#define AC_KEYB_WRITE 3 +#define AC_AC_READ 4 +#define AC_AC_WRITE 5 + +#define AUTHKEYA 0 +#define AUTHKEYB 1 +#define AUTHKEYNONE 0xff + + +static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { + uint8_t sector_trailer[16]; + emlGetMem(sector_trailer, blockNo, 1); + uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) + | ((sector_trailer[8] >> 2) & 0x02) + | ((sector_trailer[8] >> 7) & 0x01); + switch (action) { + case AC_KEYA_READ: { + return false; + break; + } + case AC_KEYA_WRITE: { + return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01)) + || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); + break; + } + case AC_KEYB_READ: { + return (keytype == AUTHKEYA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); + break; + } + case AC_KEYB_WRITE: { + return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x04)) + || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); + break; + } + case AC_AC_READ: { + return ((keytype == AUTHKEYA) + || (keytype == AUTHKEYB && !(AC == 0x00 || AC == 0x02 || AC == 0x01))); + break; + } + case AC_AC_WRITE: { + return ((keytype == AUTHKEYA && (AC == 0x01)) + || (keytype == AUTHKEYB && (AC == 0x03 || AC == 0x05))); + break; + } + 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_block; + if (blockNo < 32*4) { + sector_block = blockNo & 0x03; + } else { + sector_block = (blockNo & 0x0f) / 5; + } + + uint8_t AC; + switch (sector_block) { + case 0x00: { + AC = ((sector_trailer[7] >> 2) & 0x04) + | ((sector_trailer[8] << 1) & 0x02) + | ((sector_trailer[8] >> 4) & 0x01); + break; + } + case 0x01: { + AC = ((sector_trailer[7] >> 3) & 0x04) + | ((sector_trailer[8] >> 0) & 0x02) + | ((sector_trailer[8] >> 5) & 0x01); + break; + } + case 0x02: { + AC = ((sector_trailer[7] >> 4) & 0x04) + | ((sector_trailer[8] >> 1) & 0x02) + | ((sector_trailer[8] >> 6) & 0x01); + break; + } + default: + return false; + } + + switch (action) { + case AC_DATA_READ: { + return ((keytype == AUTHKEYA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) + || (keytype == AUTHKEYB && !(AC == 0x07))); + break; + } + case AC_DATA_WRITE: { + return ((keytype == AUTHKEYA && (AC == 0x00)) + || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03))); + break; + } + case AC_DATA_INC: { + return ((keytype == AUTHKEYA && (AC == 0x00)) + || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06))); + break; + } + case AC_DATA_DEC_TRANS_REST: { + return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) + || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06 || AC == 0x01))); + break; + } + } + + return false; +} + + +static bool IsAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { + if (IsSectorTrailer(blockNo)) { + return IsTrailerAccessAllowed(blockNo, keytype, action); + } else { + return IsDataAccessAllowed(blockNo, keytype, action); + } +} + + +static void MifareSimInit(uint8_t flags, uint8_t *datain, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len) { + + #define TAG_RESPONSE_COUNT 5 // number of precompiled responses + static uint8_t rATQA[] = {0x04, 0x00}; // indicate Mifare classic 1k 4Byte UID + static uint8_t rUIDBCC1[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level + static uint8_t rUIDBCC2[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 2nd cascade level + static uint8_t rSAKfinal[]= {0x08, 0xb6, 0xdd}; // mifare 1k indicated + static uint8_t rSAK1[] = {0x04, 0xda, 0x17}; // indicate UID not finished + + *uid_len = 4; + // UID can be set from emulator memory or incoming data and can be 4 or 7 bytes long + if (flags & FLAG_4B_UID_IN_DATA) { // get UID from datain + memcpy(rUIDBCC1, datain, 4); + } else if (flags & FLAG_7B_UID_IN_DATA) { + rUIDBCC1[0] = 0x88; + memcpy(rUIDBCC1+1, datain, 3); + memcpy(rUIDBCC2, datain+3, 4); + *uid_len = 7; + } else { + uint8_t probable_atqa; + emlGetMemBt(&probable_atqa, 7, 1); // get UID from emul memory - weak guess at length + if (probable_atqa == 0x00) { // ---------- 4BUID + emlGetMemBt(rUIDBCC1, 0, 4); + } else { // ---------- 7BUID + rUIDBCC1[0] = 0x88; + emlGetMemBt(rUIDBCC1+1, 0, 3); + emlGetMemBt(rUIDBCC2, 3, 4); + *uid_len = 7; + } + } + + switch (*uid_len) { + case 4: + *cuid = bytes_to_num(rUIDBCC1, 4); + rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + if (MF_DBGLEVEL >= 2) { + Dbprintf("4B UID: %02x%02x%02x%02x", + rUIDBCC1[0], rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3] ); + } + break; + case 7: + rATQA[0] |= 0x40; + *cuid = bytes_to_num(rUIDBCC2, 4); + rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; + if (MF_DBGLEVEL >= 2) { + Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x", + rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], rUIDBCC2[0], rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3] ); + } + break; + default: + break; + } + + static tag_response_info_t responses_init[TAG_RESPONSE_COUNT] = { + { .response = rATQA, .response_n = sizeof(rATQA) }, // Answer to request - respond with card type + { .response = rUIDBCC1, .response_n = sizeof(rUIDBCC1) }, // Anticollision cascade1 - respond with first part of uid + { .response = rUIDBCC2, .response_n = sizeof(rUIDBCC2) }, // Anticollision cascade2 - respond with 2nd part of uid + { .response = rSAKfinal, .response_n = sizeof(rSAKfinal) }, // Acknowledge select - last cascade + { .response = rSAK1, .response_n = sizeof(rSAK1) } // Acknowledge select - previous cascades + }; + + // Prepare ("precompile") the responses of the anticollision phase. There will be not enough time to do this at the moment the reader sends its REQA or SELECT + // There are 7 predefined responses with a total of 18 bytes data to transmit. Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) + // 18 * 8 data bits, 18 * 1 parity bits, 5 start bits, 5 stop bits, 5 correction bits -> need 177 bytes buffer + #define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 177 // number of bytes required for precompiled responses + + uint8_t *free_buffer_pointer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); + size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; + for (size_t i = 0; i < TAG_RESPONSE_COUNT; i++) { + prepare_allocated_tag_modulation(&responses_init[i], &free_buffer_pointer, &free_buffer_size); + } + + *responses = responses_init; + + // indices into responses array: + #define ATQA 0 + #define UIDBCC1 1 + #define UIDBCC2 2 + #define SAKfinal 3 + #define SAK1 4 + +} + + +static bool HasValidCRC(uint8_t *receivedCmd, uint16_t receivedCmd_len) { + uint8_t CRC_byte_1, CRC_byte_2; + ComputeCrc14443(CRC_14443_A, receivedCmd, receivedCmd_len-2, &CRC_byte_1, &CRC_byte_2); + return (receivedCmd[receivedCmd_len-2] == CRC_byte_1 && receivedCmd[receivedCmd_len-1] == CRC_byte_2); +} + + +/** + *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 + * FLAG_RANDOM_NONCE - means we should generate some pseudo-random nonce data (only allows moebius attack) + *@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) + */ +void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *datain) +{ + tag_response_info_t *responses; + uint8_t uid_len = 4; + uint32_t cuid = 0; + uint8_t cardWRBL = 0; + uint8_t cardAUTHSC = 0; + uint8_t cardAUTHKEY = AUTHKEYNONE; // no authentication + uint32_t cardRr = 0; + //uint32_t rn_enc = 0; + uint32_t ans = 0; + uint32_t cardINTREG = 0; + uint8_t cardINTBLOCK = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + uint32_t numReads = 0;//Counts numer of times reader reads a block + uint8_t receivedCmd[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedCmd_dec[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE]; + uint16_t receivedCmd_len; + uint8_t response[MAX_MIFARE_FRAME_SIZE]; + uint8_t response_par[MAX_MIFARE_PARITY_SIZE]; + + uint8_t rAUTH_NT[] = {0x01, 0x02, 0x03, 0x04}; + uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00}; + + //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 + 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 + uint32_t nonce; + if (flags & FLAG_RANDOM_NONCE) { + nonce = prand(); + } else { + nonce = bytes_to_num(rAUTH_NT, 4); + } + + // free eventually allocated BigBuf memory but keep Emulator Memory + BigBuf_free_keep_EM(); + + MifareSimInit(flags, datain, &responses, &cuid, &uid_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); + ResetSspClk(); + + bool finished = false; + bool button_pushed = BUTTON_PRESS(); + int cardSTATE = MFEMUL_NOFIELD; + + while (!button_pushed && !finished && !usb_poll_validate_length()) { + WDT_HIT(); + + // find reader field + if (cardSTATE == MFEMUL_NOFIELD) { + int vHf = (MAX_ADC_HF_VOLTAGE_LOW * AvgAdc(ADC_CHAN_HF_LOW)) >> 10; + if (vHf > MF_MINFIELDV) { + LED_A_ON(); + cardSTATE_TO_IDLE(); + } + button_pushed = BUTTON_PRESS(); + continue; + } + + //Now, get data + int res = EmGetCmd(receivedCmd, &receivedCmd_len, receivedCmd_par); + + if (res == 2) { //Field is off! + LEDsoff(); + cardSTATE = MFEMUL_NOFIELD; + continue; + } else if (res == 1) { // button pressed + button_pushed = true; + 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]); + + // init crypto block + crypto1_destroy(pcs); + cardAUTHKEY = AUTHKEYNONE; + if (flags & FLAG_RANDOM_NONCE) { + nonce = prand(); + } + LED_B_OFF(); + LED_C_OFF(); + cardSTATE = MFEMUL_SELECT1; + continue; + } + + switch (cardSTATE) { + case MFEMUL_NOFIELD: + case MFEMUL_HALTED: + case MFEMUL_IDLE:{ + break; + } + case MFEMUL_SELECT1:{ + // select all - 0x93 0x20 + if (receivedCmd_len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x20)) { + if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL CL1 received"); + EmSendPrecompiledCmd(&responses[UIDBCC1]); + break; + } + // select card - 0x93 0x70 ... + if (receivedCmd_len == 9 && + (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], responses[UIDBCC1].response, 4) == 0)) { + if (MF_DBGLEVEL >= 4) Dbprintf("SELECT CL1 %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); + if (uid_len == 4) { + EmSendPrecompiledCmd(&responses[SAKfinal]); + LED_B_ON(); + cardSTATE = MFEMUL_WORK; + break; + } else if (uid_len == 7) { + EmSendPrecompiledCmd(&responses[SAK1]); + cardSTATE = MFEMUL_SELECT2; + break; + } + } + cardSTATE_TO_IDLE(); + break; + } + case MFEMUL_SELECT2:{ + // select all cl2 - 0x95 0x20 + if (receivedCmd_len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x20)) { + if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL CL2 received"); + EmSendPrecompiledCmd(&responses[UIDBCC2]); + break; + } + // select cl2 card - 0x95 0x70 xxxxxxxxxxxx + if (receivedCmd_len == 9 && + (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], responses[UIDBCC2].response, 4) == 0)) { + if (uid_len == 7) { + if (MF_DBGLEVEL >= 4) Dbprintf("SELECT CL2 %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); + EmSendPrecompiledCmd(&responses[SAKfinal]); + LED_B_ON(); + cardSTATE = MFEMUL_WORK; + break; + } + } + cardSTATE_TO_IDLE(); + break; + } + case MFEMUL_WORK:{ + if (receivedCmd_len != 4) { // all commands must have exactly 4 bytes + break; + } + bool encrypted_data = (cardAUTHKEY != AUTHKEYNONE) ; + if (encrypted_data) { + // decrypt seqence + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec); + } else { + memcpy(receivedCmd_dec, receivedCmd, receivedCmd_len); + } + if (!HasValidCRC(receivedCmd_dec, receivedCmd_len)) { // all commands must have a valid CRC + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + break; + } + if (receivedCmd_dec[0] == MIFARE_AUTH_KEYA || receivedCmd_dec[0] == MIFARE_AUTH_KEYB) { + // if authenticating to a block that shouldn't exist - as long as we are not doing the reader attack + if (receivedCmd_dec[1] >= 16 * 4 && !(flags & FLAG_NR_AR_ATTACK)) { + //is this the correct response to an auth on a out of range block? marshmellow + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking",receivedCmd_dec[0],receivedCmd_dec[1],receivedCmd_dec[1]); + break; + } + cardAUTHSC = receivedCmd_dec[1] / 4; // received block num + cardAUTHKEY = receivedCmd_dec[0] & 0x01; + crypto1_destroy(pcs);//Added by martin + crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); + if (!encrypted_data) { // first authentication + if (MF_DBGLEVEL >= 4) Dbprintf("Reader authenticating for block %d (0x%02x) with key %d",receivedCmd_dec[1], receivedCmd_dec[1], cardAUTHKEY); + crypto1_word(pcs, cuid ^ nonce, 0);//Update crypto state + num_to_bytes(nonce, 4, rAUTH_AT); // Send nonce + } else { // nested authentication + if (MF_DBGLEVEL >= 4) Dbprintf("Reader doing nested authentication for block %d (0x%02x) with key %d", receivedCmd_dec[1], receivedCmd_dec[1], cardAUTHKEY); + ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0); + num_to_bytes(ans, 4, rAUTH_AT); + } + EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); + cardSTATE = MFEMUL_AUTH1; + break; + } + if (!encrypted_data) { // all other commands must be encrypted (authenticated) + break; + } + if(receivedCmd_dec[0] == ISO14443A_CMD_READBLOCK + || receivedCmd_dec[0] == ISO14443A_CMD_WRITEBLOCK + || receivedCmd_dec[0] == MIFARE_CMD_INC + || receivedCmd_dec[0] == MIFARE_CMD_DEC + || receivedCmd_dec[0] == MIFARE_CMD_RESTORE + || receivedCmd_dec[0] == MIFARE_CMD_TRANSFER) { + if (receivedCmd_dec[1] >= 16 * 4) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking",receivedCmd_dec[0],receivedCmd_dec[1],receivedCmd_dec[1]); + break; + } + if (receivedCmd_dec[1] / 4 != cardAUTHSC) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02x) on block (0x%02x) not authenticated for (0x%02x), nacking",receivedCmd_dec[0],receivedCmd_dec[1],cardAUTHSC); + break; + } + } + if (receivedCmd_dec[0] == ISO14443A_CMD_READBLOCK) { + uint8_t blockNo = receivedCmd_dec[1]; + if (MF_DBGLEVEL >= 4) { + Dbprintf("Reader reading block %d (0x%02x)", blockNo, blockNo); + } + emlGetMem(response, blockNo, 1); + if (IsSectorTrailer(blockNo)) { + memset(response, 0x00, 6); // keyA can never be read + if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_KEYB_READ)) { + memset(response+10, 0x00, 6); // keyB cannot be read + } + if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_AC_READ)) { + memset(response+6, 0x00, 4); // AC bits cannot be read + } + } else { + if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_DATA_READ)) { + memset(response, 0x00, 16); // datablock cannot be read + } + } + AppendCrc14443a(response, 16); + mf_crypto1_encrypt(pcs, response, 18, response_par); + EmSendCmdPar(response, 18, response_par); + numReads++; + if(exitAfterNReads > 0 && numReads == exitAfterNReads) { + Dbprintf("%d reads done, exiting", numReads); + finished = true; + } + break; + } + if (receivedCmd_dec[0] == ISO14443A_CMD_WRITEBLOCK) { + uint8_t blockNo = receivedCmd_dec[1]; + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0xA0 write block %d (%02x)", blockNo, blockNo); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + cardWRBL = blockNo; + cardSTATE = MFEMUL_WRITEBL2; + break; + } + if (receivedCmd_dec[0] == MIFARE_CMD_INC || receivedCmd_dec[0] == MIFARE_CMD_DEC || receivedCmd_dec[0] == MIFARE_CMD_RESTORE) { + uint8_t blockNo = receivedCmd_dec[1]; + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x)",receivedCmd_dec[0], blockNo, blockNo); + if (emlCheckValBl(blockNo)) { + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate on block, but emlCheckValBl failed, nacking"); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + break; + } + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + cardWRBL = blockNo; + if (receivedCmd_dec[0] == MIFARE_CMD_INC) + cardSTATE = MFEMUL_INTREG_INC; + if (receivedCmd_dec[0] == MIFARE_CMD_DEC) + cardSTATE = MFEMUL_INTREG_DEC; + if (receivedCmd_dec[0] == MIFARE_CMD_RESTORE) + cardSTATE = MFEMUL_INTREG_REST; + break; + } + if (receivedCmd_dec[0] == MIFARE_CMD_TRANSFER) { + uint8_t blockNo = receivedCmd_dec[1]; + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x transfer block %d (%02x)",receivedCmd_dec[0], blockNo, blockNo); + if (emlSetValBl(cardINTREG, cardINTBLOCK, receivedCmd_dec[1])) + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + else + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + break; + } + // halt + if (receivedCmd_dec[0] == ISO14443A_CMD_HALT && receivedCmd_dec[1] == 0x00) { + if (MF_DBGLEVEL >= 4) Dbprintf("--> HALTED."); + LED_B_OFF(); + LED_C_OFF(); + cardSTATE = MFEMUL_HALTED; + break; + } + // command not allowed + if (MF_DBGLEVEL >= 4) Dbprintf("Received command not allowed, nacking"); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + break; + } + case MFEMUL_AUTH1:{ + if (receivedCmd_len != 8) { + cardSTATE_TO_IDLE(); + break; + } + + uint32_t nr = bytes_to_num(receivedCmd, 4); + uint32_t ar = bytes_to_num(&receivedCmd[4], 4); + + // Collect AR/NR per keytype & sector + if(flags & 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; + if (flags & FLAG_RANDOM_NONCE) { + nonce = prand(); + } else { + 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 OK + if (cardRr != prng_successor(nonce, 64)){ + if (MF_DBGLEVEL >= 2) Dbprintf("AUTH FAILED for sector %d with key %c. cardRr=%08x, succ=%08x", + cardAUTHSC, cardAUTHKEY == AUTHKEYA ? 'A' : 'B', + cardRr, prng_successor(nonce, 64)); + // Shouldn't we respond anything here? + // Right now, we don't nack or anything, which causes the + // reader to do a WUPA after a while. /Martin + // -- which is the correct response. /piwi + cardAUTHKEY = AUTHKEYNONE; // not authenticated + cardSTATE_TO_IDLE(); + break; + } + ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0); + num_to_bytes(ans, 4, rAUTH_AT); + EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); + if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED for sector %d with key %c.", cardAUTHSC, cardAUTHKEY == AUTHKEYA ? 'A' : 'B'); + LED_C_ON(); + cardSTATE = MFEMUL_WORK; + break; + } + case MFEMUL_WRITEBL2:{ + if (receivedCmd_len == 18) { + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec); + if (HasValidCRC(receivedCmd_dec, receivedCmd_len)) { + if (IsSectorTrailer(cardWRBL)) { + emlGetMem(response, cardWRBL, 1); + if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYA_WRITE)) { + memcpy(receivedCmd_dec, response, 6); // don't change KeyA + } + if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYB_WRITE)) { + memcpy(receivedCmd_dec+10, response+10, 6); // don't change KeyA + } + if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_AC_WRITE)) { + memcpy(receivedCmd_dec+6, response+6, 4); // don't change AC bits + } + } else { + if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_DATA_WRITE)) { + memcpy(receivedCmd_dec, response, 16); // don't change anything + } + } + emlSetMem(receivedCmd_dec, cardWRBL, 1); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); // always ACK? + cardSTATE = MFEMUL_WORK; + break; + } + } + cardSTATE_TO_IDLE(); + break; + } + case MFEMUL_INTREG_INC:{ + if (receivedCmd_len == 6) { + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, (uint8_t*)&ans); + if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + cardSTATE_TO_IDLE(); + break; + } + cardINTREG = cardINTREG + ans; + } + cardSTATE = MFEMUL_WORK; + break; + } + case MFEMUL_INTREG_DEC:{ + if (receivedCmd_len == 6) { + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, (uint8_t*)&ans); + if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + cardSTATE_TO_IDLE(); + break; + } + } + cardINTREG = cardINTREG - ans; + cardSTATE = MFEMUL_WORK; + break; + } + case MFEMUL_INTREG_REST:{ + mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, (uint8_t*)&ans); + if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + cardSTATE_TO_IDLE(); + break; + } + cardSTATE = MFEMUL_WORK; + break; + } + } + button_pushed = BUTTON_PRESS(); + } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + + if(flags & FLAG_NR_AR_ATTACK && MF_DBGLEVEL >= 1) { + 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 %s from reader for sector %d:", (i= 1) Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", get_tracing(), BigBuf_get_traceLen()); + + if(flags & FLAG_INTERACTIVE) { // Interactive mode flag, means we need to send ACK + //Send the collected ar_nr in the response + cmd_send(CMD_ACK,CMD_SIMULATE_MIFARE_CARD,button_pushed,0,&ar_nr_resp,sizeof(ar_nr_resp)); + } +} diff --git a/client/data.h b/armsrc/mifaresim.h similarity index 61% rename from client/data.h rename to armsrc/mifaresim.h index 7d85e1f1..1e17a882 100644 --- a/client/data.h +++ b/armsrc/mifaresim.h @@ -1,23 +1,20 @@ //----------------------------------------------------------------------------- -// Copyright (C) 2010 iZsh +// Merlok - June 2011, 2012 +// Gerhard de Koning Gans - May 2008 +// Hagen Fritsch - June 2010 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Data utilities +// Mifare Classic Card Simulation //----------------------------------------------------------------------------- -#ifndef DATA_H__ -#define DATA_H__ +#ifndef __MIFARESIM_H +#define __MIFARESIM_H #include -#define FILE_PATH_SIZE 1000 - -extern uint8_t* sample_buf; -#define arraylen(x) (sizeof(x)/sizeof((x)[0])) - -void GetFromBigBuf(uint8_t *dest, int bytes, int start_index); +extern void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *datain); #endif diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index 7f94b0fe..5391e5f9 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -1,183 +1,166 @@ -//----------------------------------------------------------------------------- -// Merlok - 2012 -// -// This code is licensed to you under the terms of the GNU GPL, version 2 or, -// at your option, any later version. See the LICENSE.txt file for the text of -// the license. -//----------------------------------------------------------------------------- -// Routines to support mifare classic sniffer. -//----------------------------------------------------------------------------- - -#include "mifaresniff.h" -#include "apps.h" -#include "proxmark3.h" -#include "util.h" -#include "string.h" -#include "iso14443crc.h" -#include "iso14443a.h" -#include "crapto1/crapto1.h" -#include "mifareutil.h" -#include "common.h" - - -static int sniffState = SNF_INIT; -static uint8_t sniffUIDType; -static uint8_t sniffUID[8] = {0x00}; -static uint8_t sniffATQA[2] = {0x00}; -static uint8_t sniffSAK; -static uint8_t sniffBuf[16] = {0x00}; -static uint32_t timerData = 0; - - -bool MfSniffInit(void){ - memset(sniffUID, 0x00, 8); - memset(sniffATQA, 0x00, 2); - sniffSAK = 0; - sniffUIDType = SNF_UID_4; - - return FALSE; -} - -bool MfSniffEnd(void){ - LED_B_ON(); - cmd_send(CMD_ACK,0,0,0,0,0); - LED_B_OFF(); - - return FALSE; -} - -bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader) { - - if (reader && (len == 1) && (bitCnt == 7)) { // reset on 7-Bit commands from reader - sniffState = SNF_INIT; - } - - switch (sniffState) { - case SNF_INIT:{ - if ((len == 1) && (reader) && (bitCnt == 7) ) { // REQA or WUPA from reader - sniffUIDType = SNF_UID_4; - memset(sniffUID, 0x00, 8); - memset(sniffATQA, 0x00, 2); - sniffSAK = 0; - sniffState = SNF_WUPREQ; - } - break; - } - case SNF_WUPREQ:{ - if ((!reader) && (len == 2)) { // ATQA from tag - memcpy(sniffATQA, data, 2); - sniffState = SNF_ATQA; - } - break; - } - case SNF_ATQA:{ - if ((reader) && (len == 2) && (data[0] == 0x93) && (data[1] == 0x20)) { // Select ALL from reader - sniffState = SNF_ANTICOL1; - } - break; - } - case SNF_ANTICOL1:{ - if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // UID from tag (CL1) - memcpy(sniffUID + 3, data, 4); - sniffState = SNF_UID1; - } - break; - } +//----------------------------------------------------------------------------- +// Merlok - 2012 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Routines to support mifare classic sniffer. +//----------------------------------------------------------------------------- + +#include "mifaresniff.h" +#include "apps.h" +#include "proxmark3.h" +#include "util.h" +#include "string.h" +#include "iso14443crc.h" +#include "iso14443a.h" +#include "crapto1/crapto1.h" +#include "mifareutil.h" +#include "common.h" + + +static int sniffState = SNF_INIT; +static uint8_t sniffUIDType; +static uint8_t sniffUID[8] = {0x00}; +static uint8_t sniffATQA[2] = {0x00}; +static uint8_t sniffSAK; +static uint8_t sniffBuf[16] = {0x00}; +static uint32_t timerData = 0; + + +bool MfSniffInit(void){ + memset(sniffUID, 0x00, 8); + memset(sniffATQA, 0x00, 2); + sniffSAK = 0; + sniffUIDType = SNF_UID_4; + + return false; +} + +bool MfSniffEnd(void){ + LED_B_ON(); + cmd_send(CMD_ACK,0,0,0,0,0); + LED_B_OFF(); + + return false; +} + +bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader) { + + if (reader && (len == 1) && (bitCnt == 7)) { // reset on 7-Bit commands from reader + sniffState = SNF_INIT; + } + + switch (sniffState) { + case SNF_INIT:{ + if ((len == 1) && (reader) && (bitCnt == 7) ) { // REQA or WUPA from reader + sniffUIDType = SNF_UID_4; + memset(sniffUID, 0x00, 8); + memset(sniffATQA, 0x00, 2); + sniffSAK = 0; + sniffState = SNF_ATQA; + if (data[0] == 0x40) + sniffState = SNF_MAGIC_WUPC2; + } + break; + } + case SNF_MAGIC_WUPC2: + if ((len == 1) && (reader) && (data[0] == 0x43) ) { + sniffState = SNF_CARD_IDLE; + } + break; + case SNF_ATQA:{ + if ((!reader) && (len == 2)) { // ATQA from tag + memcpy(sniffATQA, data, 2); + sniffState = SNF_UID1; + } + break; + } case SNF_UID1:{ if ((reader) && (len == 9) && (data[0] == 0x93) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { // Select 4 Byte UID from reader + memcpy(sniffUID + 3, &data[2], 4); sniffState = SNF_SAK; } - break; - } - case SNF_SAK:{ - if ((!reader) && (len == 3) && (CheckCrc14443(CRC_14443_A, data, 3))) { // SAK from card? - sniffSAK = data[0]; - if (sniffUID[3] == 0x88) { // CL2 UID part to be expected - sniffState = SNF_ANTICOL2; - } else { // select completed - sniffState = SNF_CARD_IDLE; - } - } - break; - } - case SNF_ANTICOL2:{ - if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // CL2 UID - memcpy(sniffUID, sniffUID+4, 3); - memcpy(sniffUID+3, data, 4); - sniffUIDType = SNF_UID_7; - sniffState = SNF_UID2; - } - break; + break; } + case SNF_SAK:{ + if ((!reader) && (len == 3) && (CheckCrc14443(CRC_14443_A, data, 3))) { // SAK from card? + sniffSAK = data[0]; + if ((sniffUID[3] == 0x88) && (sniffUIDType == SNF_UID_4)) { // CL2 UID part to be expected + sniffUIDType = SNF_UID_7; + memcpy(sniffUID, sniffUID + 4, 3); + sniffState = SNF_UID2; + } else { // select completed + sniffState = SNF_CARD_IDLE; + } + } + break; + } case SNF_UID2:{ - if ((reader) && (len == 9) && (data[0] == 0x95) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { // Select 2nd part of 7 Byte UID + if ((reader) && (len == 9) && (data[0] == 0x95) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { + memcpy(sniffUID + 3, &data[2], 4); sniffState = SNF_SAK; } break; } - case SNF_CARD_IDLE:{ // trace the card select sequence - sniffBuf[0] = 0xFF; - sniffBuf[1] = 0xFF; - memcpy(sniffBuf + 2, sniffUID, 7); - memcpy(sniffBuf + 9, sniffATQA, 2); - sniffBuf[11] = sniffSAK; - sniffBuf[12] = 0xFF; - sniffBuf[13] = 0xFF; - LogTrace(sniffBuf, 14, 0, 0, NULL, TRUE); - } // intentionally no break; - case SNF_CARD_CMD:{ - LogTrace(data, len, 0, 0, NULL, TRUE); - sniffState = SNF_CARD_RESP; - timerData = GetTickCount(); - break; - } - case SNF_CARD_RESP:{ - LogTrace(data, len, 0, 0, NULL, FALSE); - sniffState = SNF_CARD_CMD; - timerData = GetTickCount(); - break; - } - - default: - sniffState = SNF_INIT; - break; - } - - - return FALSE; -} - -bool RAMFUNC MfSniffSend(uint16_t maxTimeoutMs) { - if (BigBuf_get_traceLen() && (GetTickCount() > timerData + maxTimeoutMs)) { - return intMfSniffSend(); - } - return FALSE; -} - -// internal sending function. not a RAMFUNC. -bool intMfSniffSend() { - - int pckSize = 0; - int pckLen = BigBuf_get_traceLen(); - int pckNum = 0; - uint8_t *trace = BigBuf_get_addr(); - - FpgaDisableSscDma(); - while (pckLen > 0) { - pckSize = MIN(USB_CMD_DATA_SIZE, pckLen); - LED_B_ON(); - cmd_send(CMD_ACK, 1, BigBuf_get_traceLen(), pckSize, trace + BigBuf_get_traceLen() - pckLen, pckSize); - LED_B_OFF(); - - pckLen -= pckSize; - pckNum++; - } - - LED_B_ON(); - cmd_send(CMD_ACK,2,0,0,0,0); - LED_B_OFF(); - - clear_trace(); - - return TRUE; -} + case SNF_CARD_IDLE:{ // trace the card select sequence + sniffBuf[0] = 0xFF; + sniffBuf[1] = 0xFF; + memcpy(sniffBuf + 2, sniffUID, 7); + memcpy(sniffBuf + 9, sniffATQA, 2); + sniffBuf[11] = sniffSAK; + sniffBuf[12] = 0xFF; + sniffBuf[13] = 0xFF; + LogTrace(sniffBuf, 14, 0, 0, NULL, true); + sniffState = SNF_CARD_CMD; + } // intentionally no break; + case SNF_CARD_CMD:{ + LogTrace(data, len, 0, 0, parity, reader); + timerData = GetTickCount(); + break; + } + + default: + sniffState = SNF_INIT; + break; + } + + + return false; +} + +bool RAMFUNC MfSniffSend(uint16_t maxTimeoutMs) { + if (BigBuf_get_traceLen() && (GetTickCount() > timerData + maxTimeoutMs)) { + return intMfSniffSend(); + } + return false; +} + +// internal sending function. not a RAMFUNC. +bool intMfSniffSend() { + + int pckSize = 0; + int pckLen = BigBuf_get_traceLen(); + int pckNum = 0; + uint8_t *trace = BigBuf_get_addr(); + + FpgaDisableSscDma(); + while (pckLen > 0) { + pckSize = MIN(USB_CMD_DATA_SIZE, pckLen); + LED_B_ON(); + cmd_send(CMD_ACK, 1, BigBuf_get_traceLen(), pckSize, trace + BigBuf_get_traceLen() - pckLen, pckSize); + LED_B_OFF(); + + pckLen -= pckSize; + pckNum++; + } + + LED_B_ON(); + cmd_send(CMD_ACK,2,0,0,0,0); + LED_B_OFF(); + + clear_trace(); + + return true; +} diff --git a/armsrc/mifaresniff.h b/armsrc/mifaresniff.h index 8a8e31a9..b181f982 100644 --- a/armsrc/mifaresniff.h +++ b/armsrc/mifaresniff.h @@ -27,6 +27,7 @@ #define SNF_CARD_IDLE 9 #define SNF_CARD_CMD 10 #define SNF_CARD_RESP 11 +#define SNF_MAGIC_WUPC2 12 #define SNF_UID_4 0 #define SNF_UID_7 0 diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index fdead5ee..684b5e36 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -9,13 +9,15 @@ // Work with mifare cards. //----------------------------------------------------------------------------- -#include #include "mifareutil.h" + +#include +#include + #include "proxmark3.h" #include "apps.h" #include "util.h" #include "parity.h" - #include "iso14443crc.h" #include "iso14443a.h" #include "crapto1/crapto1.h" @@ -24,23 +26,27 @@ int MF_DBGLEVEL = MF_DBG_ALL; // crypto1 helpers -void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len){ +void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out){ uint8_t bt = 0; int i; if (len != 1) { for (i = 0; i < len; i++) - data[i] = crypto1_byte(pcs, 0x00, 0) ^ data[i]; + data_out[i] = crypto1_byte(pcs, 0x00, 0) ^ data_in[i]; } else { bt = 0; for (i = 0; i < 4; i++) - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data[0], i)) << i; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], i)) << i; - data[0] = bt; + data_out[0] = bt; } return; } +void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len){ + mf_crypto1_decryptEx(pcs, data, len, data); +} + void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par) { uint8_t bt = 0; int i; @@ -399,31 +405,48 @@ int mifare_ultra_auth(uint8_t *keybytes){ return 1; } + +#define MFU_MAX_RETRIES 5 int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { uint16_t len; uint8_t bt[2]; uint8_t receivedAnswer[MAX_FRAME_SIZE]; uint8_t receivedAnswerPar[MAX_PARITY_SIZE]; - + uint8_t retries; + int result = 0; - len = mifare_sendcmd_short(NULL, 1, 0x30, blockNo, receivedAnswer, receivedAnswerPar, NULL); - if (len == 1) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; + for (retries = 0; retries < MFU_MAX_RETRIES; retries++) { + len = mifare_sendcmd_short(NULL, 1, 0x30, blockNo, receivedAnswer, receivedAnswerPar, NULL); + if (len == 1) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + result = 1; + continue; + } + if (len != 18) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: card timeout. len: %x", len); + result = 2; + continue; + } + + memcpy(bt, receivedAnswer + 16, 2); + AppendCrc14443a(receivedAnswer, 16); + if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd CRC response error."); + result = 3; + continue; + } + + // No errors encountered; don't retry + result = 0; + break; } - if (len != 18) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: card timeout. len: %x", len); - return 2; + + if (result != 0) { + Dbprintf("Cmd Error: too many retries; read failed"); + return result; } - - memcpy(bt, receivedAnswer + 16, 2); - AppendCrc14443a(receivedAnswer, 16); - if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd CRC response error."); - return 3; - } - + memcpy(blockData, receivedAnswer, 14); return 0; } @@ -581,6 +604,19 @@ uint8_t FirstBlockOfSector(uint8_t sectorNo) } +uint8_t SectorTrailer(uint8_t blockNo) +{ + if (blockNo < 32*4) { + return (blockNo | 0x03); + } else { + return (blockNo | 0x0f); + } +} + +bool IsSectorTrailer(uint8_t blockNo) +{ + return (blockNo == SectorTrailer(blockNo)); +} // work with emulator memory void emlSetMem(uint8_t *data, int blockNum, int blocksCount) { @@ -764,3 +800,125 @@ int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData){ } return 1; } + +//----------------------------------------------------------------------------- +// MIFARE check keys +// +//----------------------------------------------------------------------------- +// one key check +int MifareChkBlockKey(uint8_t *uid, uint32_t *cuid, uint8_t *cascade_levels, uint64_t ui64Key, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel) { + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + // Iceman: use piwi's faster nonce collecting part in hardnested. + if (*cascade_levels == 0) { // 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 (debugLevel >= 1) Dbprintf("ChkKeys: Can't select card"); + return 1; + } + switch (card_info.uidlen) { + case 4 : *cascade_levels = 1; break; + case 7 : *cascade_levels = 2; break; + case 10: *cascade_levels = 3; break; + default: break; + } + } else { // no need for anticollision. We can directly select the card + if(!iso14443a_select_card(uid, NULL, NULL, false, *cascade_levels, true)) { + if (debugLevel >= 1) Dbprintf("ChkKeys: Can't select card (UID) lvl=%d", *cascade_levels); + return 1; + } + } + + if(mifare_classic_auth(pcs, *cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { +// SpinDelayUs(AUTHENTICATION_TIMEOUT); // it not needs because mifare_classic_auth have timeout from iso14a_set_timeout() + return 2; + } else { +/* // let it be here. it like halt command, but maybe it will work in some strange cases + uint8_t dummy_answer = 0; + ReaderTransmit(&dummy_answer, 1, NULL); + int timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; + // wait for the card to become ready again + while(GetCountSspClk() < timeout) {}; +*/ + // it needs after success authentication + mifare_classic_halt(pcs, *cuid); + } + + return 0; +} + +// multi key check +int MifareChkBlockKeys(uint8_t *keys, uint8_t keyCount, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel) { + uint8_t uid[10]; + uint32_t cuid = 0; + uint8_t cascade_levels = 0; + uint64_t ui64Key = 0; + + int retryCount = 0; + for (uint8_t i = 0; i < keyCount; i++) { + + // Allow button press / usb cmd to interrupt device + if (BUTTON_PRESS() && !usb_poll_validate_length()) { + Dbprintf("ChkKeys: Cancel operation. Exit..."); + return -2; + } + + ui64Key = bytes_to_num(keys + i * 6, 6); + int res = MifareChkBlockKey(uid, &cuid, &cascade_levels, ui64Key, blockNo, keyType, debugLevel); + + // can't select + if (res == 1) { + retryCount++; + if (retryCount >= 5) { + Dbprintf("ChkKeys: block=%d key=%d. Can't select. Exit...", blockNo, keyType); + return -1; + } + --i; // try the same key once again + + SpinDelay(20); +// Dbprintf("ChkKeys: block=%d key=%d. Try the same key once again...", blockNo, keyType); + continue; + } + + // can't authenticate + if (res == 2) { + retryCount = 0; + continue; // can't auth. wrong key. + } + + return i + 1; + } + + return 0; +} + +// multisector multikey check +int MifareMultisectorChk(uint8_t *keys, uint8_t keyCount, uint8_t SectorCount, uint8_t keyType, uint8_t debugLevel, TKeyIndex *keyIndex) { + int res = 0; + +// int clk = GetCountSspClk(); + + for(int sc = 0; sc < SectorCount; sc++){ + WDT_HIT(); + + int keyAB = keyType; + do { + res = MifareChkBlockKeys(keys, keyCount, FirstBlockOfSector(sc), keyAB & 0x01, debugLevel); + if (res < 0){ + return res; + } + if (res > 0){ + (*keyIndex)[keyAB & 0x01][sc] = res; + } + } while(--keyAB > 0); + } + +// Dbprintf("%d %d", GetCountSspClk() - clk, (GetCountSspClk() - clk)/(SectorCount*keyCount*(keyType==2?2:1))); + + return 0; +} + + diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index 468c5cce..b2912895 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -12,7 +12,11 @@ #ifndef __MIFAREUTIL_H #define __MIFAREUTIL_H +#include +#include + #include "crapto1/crapto1.h" +#include "usb_cdc.h" // mifare authentication #define CRYPT_NONE 0 @@ -38,23 +42,6 @@ extern int MF_DBGLEVEL; -//mifare emulator states -#define MFEMUL_NOFIELD 0 -#define MFEMUL_IDLE 1 -#define MFEMUL_SELECT1 2 -#define MFEMUL_SELECT2 3 -#define MFEMUL_SELECT3 4 -#define MFEMUL_AUTH1 5 -#define MFEMUL_AUTH2 6 -#define MFEMUL_WORK 7 -#define MFEMUL_WRITEBL2 8 -#define MFEMUL_INTREG_INC 9 -#define MFEMUL_INTREG_DEC 10 -#define MFEMUL_INTREG_REST 11 -#define MFEMUL_HALTED 12 - -#define cardSTATE_TO_IDLE() cardSTATE = MFEMUL_IDLE; LED_B_OFF(); LED_C_OFF(); - //functions int mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); @@ -82,12 +69,15 @@ int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData); // crypto functions void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *receivedCmd, int len); +void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out); void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par); uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data); // Mifare memory structure uint8_t NumBlocksPerSector(uint8_t sectorNo); uint8_t FirstBlockOfSector(uint8_t sectorNo); +bool IsSectorTrailer(uint8_t blockNo); +uint8_t SectorTrailer(uint8_t blockNo); // emulator functions void emlClearMem(void); @@ -99,4 +89,10 @@ int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum); int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum); int emlCheckValBl(int blockNum); +// mifare check keys +typedef uint8_t TKeyIndex[2][40]; +int MifareChkBlockKey(uint8_t *uid, uint32_t *cuid, uint8_t *cascade_levels, uint64_t ui64Key, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel); +int MifareChkBlockKeys(uint8_t *keys, uint8_t keyCount, uint8_t blockNo, uint8_t keyType, uint8_t debugLevel); +int MifareMultisectorChk(uint8_t *keys, uint8_t keyCount, uint8_t SectorCount, uint8_t keyType, uint8_t debugLevel, TKeyIndex *keyIndex); + #endif diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 005f473b..b1f33737 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/armsrc/optimized_cipher.h b/armsrc/optimized_cipher.h index c10aea28..6a4e2641 100644 --- a/armsrc/optimized_cipher.h +++ b/armsrc/optimized_cipher.h @@ -1,4 +1,41 @@ -#ifndef OPTIMIZED_CIPHER_H +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ + + #ifndef OPTIMIZED_CIPHER_H #define OPTIMIZED_CIPHER_H #include diff --git a/armsrc/printf.c b/armsrc/printf.c index 94ed809d..79c123df 100644 --- a/armsrc/printf.c +++ b/armsrc/printf.c @@ -175,6 +175,7 @@ reswitch: switch (ch = (u_char)*fmt++) { padc = '0'; goto reswitch; } + // intentionally fall through to next case case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (n = 0;; ++fmt) { diff --git a/armsrc/util.c b/armsrc/util.c index a0fa50d3..fbb6d489 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -81,11 +81,6 @@ void lsl (uint8_t *data, size_t len) { data[len - 1] <<= 1; } -int32_t le24toh (uint8_t data[3]) -{ - return (data[2] << 16) | (data[1] << 8) | data[0]; -} - void LEDsoff() { LED_A_OFF(); @@ -292,6 +287,7 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers strncat(dst, "\n", len - strlen(dst) - 1); } + // ------------------------------------------------------------------------- // timer lib // ------------------------------------------------------------------------- @@ -312,6 +308,7 @@ void StartTickCount() // note: worst case precision is approx 2.5% } + /* * Get the current count. */ @@ -319,6 +316,7 @@ uint32_t RAMFUNC GetTickCount(){ return AT91C_BASE_RTTC->RTTC_RTVR;// was * 2; } + // ------------------------------------------------------------------------- // microseconds timer // ------------------------------------------------------------------------- @@ -344,10 +342,12 @@ void StartCountUS() AT91C_BASE_TCB->TCB_BCR = 1; } + uint32_t RAMFUNC GetCountUS(){ return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV * 2) / 3); //was /15) * 10); } + static uint32_t GlobalUsCounter = 0; uint32_t RAMFUNC GetDeltaCountUS(){ @@ -373,13 +373,13 @@ void StartCountSspClk() AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK // TC1 Clock = MCK(48MHz)/2 = 24MHz | AT91C_TC_CPCSTOP // Stop clock on RC compare | AT91C_TC_EEVTEDG_RISING // Trigger on rising edge of Event - | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16) + | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16 ... 13,56MHz/4) | AT91C_TC_ENETRG // Enable external trigger event | AT91C_TC_WAVESEL_UP // Upmode without automatic trigger on RC compare | AT91C_TC_WAVE // Waveform Mode | AT91C_TC_AEEVT_SET // Set TIOA1 on external event | AT91C_TC_ACPC_CLEAR; // Clear TIOA1 on RC Compare - AT91C_BASE_TC1->TC_RC = 0x04; // RC Compare value = 0x04 + AT91C_BASE_TC1->TC_RC = 0x02; // RC Compare value = 0x02 // use TC0 to count TIOA1 pulses AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // disable TC0 @@ -402,7 +402,7 @@ void StartCountSspClk() AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN; // enable TC2 // - // synchronize the counter with the ssp_frame signal. Note: FPGA must be in any iso14446 mode, otherwise the frame signal would not be present + // synchronize the counter with the ssp_frame signal. Note: FPGA must be in a FPGA mode with SSC transfer, otherwise SSC_FRAME and SSC_CLK signals would not be present // while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low @@ -416,8 +416,11 @@ void StartCountSspClk() // (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. - while (AT91C_BASE_TC0->TC_CV < 0xFFF0); + while (AT91C_BASE_TC0->TC_CV < 0xFFFF); + // Note: needs one more SSP_CLK cycle (1.18 us) until TC2 resets. Don't call GetCountSspClk() that soon. } + + void ResetSspClk(void) { //enable clock of timer and software trigger AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; @@ -425,78 +428,112 @@ void ResetSspClk(void) { AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; while (AT91C_BASE_TC2->TC_CV > 0); } -uint32_t RAMFUNC GetCountSspClk(){ - uint32_t tmp_count; - tmp_count = (AT91C_BASE_TC2->TC_CV << 16) | AT91C_BASE_TC0->TC_CV; - if ((tmp_count & 0x0000ffff) == 0) { //small chance that we may have missed an increment in TC2 - return (AT91C_BASE_TC2->TC_CV << 16); - } - else { - return tmp_count; - } + + +uint32_t GetCountSspClk(){ + uint32_t hi, lo; + + do { + hi = AT91C_BASE_TC2->TC_CV; + lo = AT91C_BASE_TC0->TC_CV; + } while(hi != AT91C_BASE_TC2->TC_CV); + + return (hi << 16) | lo; } + // ------------------------------------------------------------------------- -// Timer for bitbanging, or LF stuff when you need a very precis timer +// Timer for bitbanging, or LF stuff when you need a very precis timer // 1us = 1.5ticks // ------------------------------------------------------------------------- void StartTicks(void){ - //initialization of the timer - // tc1 is higher 0xFFFF0000 - // tc0 is lower 0x0000FFFF + // initialization of the timer AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); 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; - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32 - AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | - AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; - AT91C_BASE_TC0->TC_RA = 1; - AT91C_BASE_TC0->TC_RC = 0; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from TC0 - - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TCB->TCB_BCR = 1; - - // wait until timer becomes zero. - while (AT91C_BASE_TC1->TC_CV > 0); + // disable TC0 and TC1 for re-configuration + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // first configure TC1 (higher, 0xFFFF0000) 16 bit counter + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // just connect to TIOA0 from TC0 + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // re-enable timer and wait for TC0 + + // second configure TC0 (lower, 0x0000FFFF) 16 bit counter + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32 + AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | + AT91C_TC_ACPA_CLEAR | // RA comperator clears TIOA (carry bit) + AT91C_TC_ACPC_SET | // RC comperator sets TIOA (carry bit) + AT91C_TC_ASWTRG_SET; // SWTriger sets TIOA (carry bit) + AT91C_BASE_TC0->TC_RC = 0; // set TIOA (carry bit) on overflow, return to zero + AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // reset and re-enable timer + + // synchronized startup procedure + 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) + + // return to zero + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV > 0); } + +uint32_t GetTicks(void) { + uint32_t hi, lo; + + do { + hi = AT91C_BASE_TC1->TC_CV; + lo = AT91C_BASE_TC0->TC_CV; + } while(hi != AT91C_BASE_TC1->TC_CV); + + return (hi << 16) | lo; +} + + // Wait - Spindelay in ticks. // if called with a high number, this will trigger the WDT... void WaitTicks(uint32_t ticks){ if ( ticks == 0 ) return; - ticks += GET_TICKS; - while (GET_TICKS < ticks); + ticks += GetTicks(); + while (GetTicks() < ticks); } + + // Wait / Spindelay in us (microseconds) // 1us = 1.5ticks. void WaitUS(uint16_t us){ - if ( us == 0 ) return; - WaitTicks( (uint32_t)(us * 1.5) ); + WaitTicks( (uint32_t)us * 3 / 2 ) ; } + + void WaitMS(uint16_t ms){ - if (ms == 0) return; - WaitTicks( (uint32_t)(ms * 1500) ); + WaitTicks( (uint32_t)ms * 1500 ); } + + // Starts Clock and waits until its reset void ResetTicks(void){ - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - while (AT91C_BASE_TC1->TC_CV > 0); + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV > 0); } + + void ResetTimer(AT91PS_TC timer){ timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; while(timer->TC_CV > 0) ; } + + // stop clock void StopTicks(void){ AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; } + static uint64_t next_random = 1; /* Generates a (non-cryptographically secure) 32-bit random number. @@ -512,4 +549,3 @@ uint32_t prand() { next_random = next_random * 6364136223846793005 + 1; return (uint32_t)(next_random >> 32) % 0xffffffff; } - diff --git a/armsrc/util.h b/armsrc/util.h index 0148e1dd..fb50ecc8 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -34,7 +34,6 @@ void num_to_bytes(uint64_t n, size_t len, uint8_t* dest); uint64_t bytes_to_num(uint8_t* src, size_t len); void rol(uint8_t *data, const size_t len); void lsl (uint8_t *data, size_t len); -int32_t le24toh (uint8_t data[3]); void LED(int led, int ms); void LEDsoff(); @@ -44,7 +43,7 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers //iceman's ticks.h #ifndef GET_TICKS -# define GET_TICKS (uint32_t)((AT91C_BASE_TC1->TC_CV << 16) | AT91C_BASE_TC0->TC_CV) +# define GET_TICKS GetTicks() #endif void SpinDelay(int ms); @@ -62,6 +61,7 @@ void ResetSspClk(void); uint32_t RAMFUNC GetCountSspClk(); extern void StartTicks(void); +extern uint32_t GetTicks(void); extern void WaitTicks(uint32_t ticks); extern void WaitUS(uint16_t us); extern void WaitMS(uint16_t ms); diff --git a/bootrom/Makefile b/bootrom/Makefile index 92373995..dd1e7e08 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -10,6 +10,7 @@ ARMSRC = THUMBSRC = cmd.c usb_cdc.c bootrom.c ASMSRC = ram-reset.s flash-reset.s +VERSIONSRC = version.c ## There is a strange bug with the linker: Sometimes it will not emit the glue to call ## BootROM from ARM mode. The symbol is emitted, but the section will be filled with @@ -21,6 +22,11 @@ ASMSRC = ram-reset.s flash-reset.s # stdint.h provided locally until GCC 4.5 becomes C99 compliant APP_CFLAGS = -I. +# version.c should be remade on every compilation +.PHONY: version.c +version.c: default_version.c + perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ + # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC include ../common/Makefile.common diff --git a/client/Makefile b/client/Makefile index fd2ae290..1b211d4f 100644 --- a/client/Makefile +++ b/client/Makefile @@ -12,23 +12,37 @@ TARFLAGS = -C .. --ignore-failed-read -rvf RM = rm -f MV = mv -#COMMON_FLAGS = -m32 +ENV_LDFLAGS := $(LDFLAGS) +ENV_CFLAGS := $(CFLAGS) VPATH = ../common ../zlib ../uart OBJDIR = obj LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LUALIB = ../liblua/liblua.a -LDFLAGS = $(COMMON_FLAGS) -CFLAGS = -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall $(COMMON_FLAGS) -g -O3 +JANSSONLIBPATH = ./jansson +JANSSONLIB = $(JANSSONLIBPATH)/libjansson.a +LDFLAGS = $(ENV_LDFLAGS) +CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua -I$(JANSSONLIBPATH) -Wall -g -O3 CXXFLAGS = -I../include -Wall -O3 +APP_CFLAGS = +include ../common/Makefile_Enabled_Options.common +CFLAGS += $(APP_CFLAGS) +ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS))) + SRC_SMARTCARD = cmdsmartcard.c +else + SRC_SMARTCARD = +endif + LUAPLATFORM = generic platform = $(shell uname) ifneq (,$(findstring MINGW,$(platform))) - LUAPLATFORM = mingw + LUAPLATFORM = mingw else ifeq ($(platform),Darwin) LUAPLATFORM = macosx + OBJCSRCS = util_darwin.m + LDFLAGS += -framework Foundation -framework AppKit else LUALIB += -ldl LDLIBS += -ltermcap -lncurses @@ -36,32 +50,34 @@ else endif endif -# Check for correctly configured Qt5 -QTINCLUDES = $(shell pkg-config --cflags Qt5Core Qt5Widgets 2>/dev/null) -QTLDLIBS = $(shell pkg-config --libs Qt5Core Qt5Widgets 2>/dev/null) -MOC = $(shell pkg-config --variable=host_bins Qt5Core)/moc -UIC = $(shell pkg-config --variable=host_bins Qt5Core)/uic -ifeq ($(QTINCLUDES), ) -# if Qt5 not found check for correctly configured Qt4 - QTINCLUDES = $(shell pkg-config --cflags QtCore QtGui 2>/dev/null) - QTLDLIBS = $(shell pkg-config --libs QtCore QtGui 2>/dev/null) - MOC = $(shell pkg-config --variable=moc_location QtCore) - UIC = $(shell pkg-config --variable=uic_location QtCore) -else - CXXFLAGS += -std=c++11 -fPIC -endif -ifeq ($(QTINCLUDES), ) -# if both pkg-config commands failed, search in common places - ifneq ($(QTDIR), ) - QTINCLUDES = -I$(QTDIR)/include -I$(QTDIR)/include/QtCore -I$(QTDIR)/include/QtGui - QTLDLIBS = -L$(QTDIR)/lib -lQtCore4 -lQtGui4 - ifneq ($(wildcard $(QTDIR)/include/QtWidgets),) - QTINCLUDES += -I$(QTDIR)/include/QtWidgets - QTLDLIBS = -L$(QTDIR)/lib -lQt5Widgets -lQt5Gui -lQt5Core - CXXFLAGS += -std=c++11 -fPIC +ifneq (,$(findstring WITH_GUI,$(APP_CFLAGS))) + # Check for correctly configured Qt5 + QTINCLUDES = $(shell pkg-config --cflags Qt5Core Qt5Widgets 2>/dev/null) + QTLDLIBS = $(shell pkg-config --libs Qt5Core Qt5Widgets 2>/dev/null) + MOC = $(shell pkg-config --variable=host_bins Qt5Core)/moc + UIC = $(shell pkg-config --variable=host_bins Qt5Core)/uic + ifeq ($(QTINCLUDES), ) + # if Qt5 not found check for correctly configured Qt4 + QTINCLUDES = $(shell pkg-config --cflags QtCore QtGui 2>/dev/null) + QTLDLIBS = $(shell pkg-config --libs QtCore QtGui 2>/dev/null) + MOC = $(shell pkg-config --variable=moc_location QtCore) + UIC = $(shell pkg-config --variable=uic_location QtCore) + else + CXXFLAGS += -std=c++11 -fPIC + endif + ifeq ($(QTINCLUDES), ) + # if both pkg-config commands failed, search in common places + ifneq ($(QTDIR), ) + QTINCLUDES = -I$(QTDIR)/include -I$(QTDIR)/include/QtCore -I$(QTDIR)/include/QtGui + QTLDLIBS = -L$(QTDIR)/lib -lQtCore4 -lQtGui4 + ifneq ($(wildcard $(QTDIR)/include/QtWidgets),) + QTINCLUDES += -I$(QTDIR)/include/QtWidgets + QTLDLIBS = -L$(QTDIR)/lib -lQt5Widgets -lQt5Gui -lQt5Core + CXXFLAGS += -std=c++11 -fPIC + endif + MOC = $(QTDIR)/bin/moc + UIC = $(QTDIR)/bin/uic endif - MOC = $(QTDIR)/bin/moc - UIC = $(QTDIR)/bin/uic endif endif @@ -78,15 +94,26 @@ DEPFLAGS = -MT $@ -MMD -MP -MF $(OBJDIR)/$*.Td # make temporary to final dependeny files after successful compilation POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d + CORESRCS = uart_posix.c \ uart_win32.c \ util.c \ - util_posix.c + util_posix.c \ + ui.c \ + comms.c -CMDSRCS = crapto1/crapto1.c\ +CMDSRCS = $(SRC_SMARTCARD) \ + crapto1/crapto1.c\ crapto1/crypto1.c\ polarssl/des.c \ polarssl/aes.c\ + polarssl/aes_cmac128.c\ + polarssl/bignum.c\ + polarssl/rsa.c\ + polarssl/sha1.c\ + polarssl/libpcrypto.c\ + cliparser/argtable3.c\ + cliparser/cliparser.c\ mfkey.c\ loclass/cipher.c \ loclass/cipherutils.c \ @@ -95,18 +122,36 @@ CMDSRCS = crapto1/crapto1.c\ loclass/fileutils.c\ whereami.c\ mifarehost.c\ + mifare4.c\ parity.c\ crc.c \ crc16.c \ crc64.c \ iso14443crc.c \ iso15693tools.c \ - data.c \ graph.c \ - ui.c \ cmddata.c \ lfdemod.c \ + emv/crypto_polarssl.c\ + emv/crypto.c\ + emv/emv_pk.c\ + emv/emv_pki.c\ + emv/emv_pki_priv.c\ + emv/test/cryptotest.c\ + emv/apduinfo.c\ + emv/dump.c\ + emv/tlv.c\ + emv/emv_tags.c\ + emv/dol.c\ + emv/emvjson.c\ + emv/emvcore.c\ + emv/test/crypto_test.c\ + emv/test/sda_test.c\ + emv/test/dda_test.c\ + emv/test/cda_test.c\ + emv/cmdemv.c\ cmdhf.c \ + cmdhflist.c \ cmdhf14a.c \ cmdhf14b.c \ cmdhf15.c \ @@ -114,6 +159,7 @@ CMDSRCS = crapto1/crapto1.c\ cmdhflegic.c \ cmdhficlass.c \ cmdhfmf.c \ + cmdhfmfp.c \ cmdhfmfu.c \ cmdhfmfhard.c \ hardnested/hardnested_bruteforce.c \ @@ -125,6 +171,8 @@ CMDSRCS = crapto1/crapto1.c\ cmdlfem4x.c \ cmdlffdx.c \ cmdlfgproxii.c \ + hidcardformatutils.c\ + hidcardformats.c\ cmdlfhid.c \ cmdlfhitag.c \ cmdlfio.c \ @@ -148,15 +196,7 @@ CMDSRCS = crapto1/crapto1.c\ cmdscript.c\ pm3_binlib.c\ pm3_bitlib.c\ - protocols.c\ - sha1.c\ - cmdcrc.c\ - reveng/reveng.c\ - reveng/cli.c\ - reveng/bmpbit.c\ - reveng/model.c\ - reveng/poly.c\ - reveng/getopt.c\ + protocols.c cpu_arch = $(shell uname -m) ifneq ($(findstring 86, $(cpu_arch)), ) @@ -177,6 +217,7 @@ QTGUISRCS = proxgui.cpp proxguiqt.cpp proxguiqt.moc.cpp guidummy.cpp COREOBJS = $(CORESRCS:%.c=$(OBJDIR)/%.o) CMDOBJS = $(CMDSRCS:%.c=$(OBJDIR)/%.o) +OBJCOBJS = $(OBJCSRCS:%.m=$(OBJDIR)/%.o) ZLIBOBJS = $(ZLIBSRCS:%.c=$(OBJDIR)/%.o) MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_MMX.o) \ @@ -184,14 +225,7 @@ MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX2.o) -GCC_VERSION := $(shell gcc --version | awk '/gcc/{print $$NF;}' | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') -CLANG_VERSION := $(shell gcc --version | awk '/Apple LLVM version/{print $$4;}' | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') -ifneq ($(CLANG_VERSION), ) - SUPPORTS_AVX512 := $(shell [ $(CLANG_VERSION) -ge 80000 ] && echo "True" ) -endif -ifneq ($(GCC_VERSION), ) - SUPPORTS_AVX512 := $(shell [ $(GCC_VERSION) -ge 40900 ] && echo "True" ) -endif +SUPPORTS_AVX512 := $(shell echo | gcc -E -mavx512f - > /dev/null 2>&1 && echo "True" ) HARD_SWITCH_NOSIMD = -mno-mmx -mno-sse2 -mno-avx -mno-avx2 HARD_SWITCH_MMX = -mmmx -mno-sse2 -mno-avx -mno-avx2 HARD_SWITCH_SSE2 = -mmmx -msse2 -mno-avx -mno-avx2 @@ -209,19 +243,19 @@ endif BINS = proxmark3 flasher fpga_compress WINBINS = $(patsubst %, %.exe, $(BINS)) -CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h +CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h # need to assign dependancies to build these first... -all: lua_build $(BINS) +all: lua_build jansson_build $(BINS) all-static: LDLIBS:=-static $(LDLIBS) all-static: proxmark3 flasher fpga_compress -proxmark3: LDLIBS+=$(LUALIB) $(QTLDLIBS) -proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/usb_cmd.lua - $(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@ +proxmark3: LDLIBS+=$(LUALIB) $(JANSSONLIB) $(QTLDLIBS) +proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/usb_cmd.lua + $(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@ -flasher: $(OBJDIR)/flash.o $(OBJDIR)/flasher.o $(COREOBJS) +flasher: $(OBJDIR)/flash.o $(OBJDIR)/flasher.o $(COREOBJS) $(OBJCOBJS) $(LD) $(LDFLAGS) $^ $(LDLIBS) -o $@ fpga_compress: $(OBJDIR)/fpga_compress.o $(ZLIBOBJS) @@ -241,6 +275,7 @@ lualibs/usb_cmd.lua: ../include/usb_cmd.h clean: $(RM) $(CLEAN) cd ../liblua && make clean + cd ./jansson && make clean tarbin: $(BINS) $(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(BINS:%=client/%) $(WINBINS:%=client/%) @@ -248,6 +283,10 @@ tarbin: $(BINS) lua_build: @echo Compiling liblua, using platform $(LUAPLATFORM) cd ../liblua && make $(LUAPLATFORM) + +jansson_build: + @echo Compiling jansson + cd ./jansson && make all .PHONY: all clean @@ -279,6 +318,10 @@ $(OBJDIR)/%.o : %.cpp $(OBJDIR)/%.d $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(QTINCLUDES) -c -o $@ $< $(POSTCOMPILE) +%.o: %.m +$(OBJDIR)/%.o : %.m $(OBJDIR)/%.d + $(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $< + $(POSTCOMPILE) #$(CMDOBJS) $(COREOBJS): $(notdir $(%.c)) %.d # $(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $< @@ -294,10 +337,10 @@ $(OBJDIR)/%.o : %.cpp $(OBJDIR)/%.d DEPENDENCY_FILES = $(patsubst %.c, $(OBJDIR)/%.d, $(CORESRCS) $(CMDSRCS) $(ZLIBSRCS) $(MULTIARCHSRCS)) \ $(patsubst %.cpp, $(OBJDIR)/%.d, $(QTGUISRCS)) \ + $(patsubst %.m, $(OBJDIR)/%.d, $(OBJCSRCS)) \ $(OBJDIR)/proxmark3.d $(OBJDIR)/flash.d $(OBJDIR)/flasher.d $(OBJDIR)/fpga_compress.d $(DEPENDENCY_FILES): ; .PRECIOUS: $(DEPENDENCY_FILES) -include $(DEPENDENCY_FILES) - diff --git a/client/cliparser/README.md b/client/cliparser/README.md new file mode 100644 index 00000000..2b321946 --- /dev/null +++ b/client/cliparser/README.md @@ -0,0 +1,13 @@ +# Command line parser + +cliparser - librari for proxmark with command line parsing high level functions. + +## External libraries: + +### argtable + +Argtable3 is a single-file, ANSI C, command-line parsing library that parses GNU-style command-line options. + +You can download argtable3 from this repository https://github.com/argtable/argtable3 + +[argtable3 license](https://github.com/argtable/argtable3/blob/master/LICENSE) diff --git a/client/cliparser/argtable3.c b/client/cliparser/argtable3.c new file mode 100644 index 00000000..f70f68c7 --- /dev/null +++ b/client/cliparser/argtable3.c @@ -0,0 +1,5005 @@ +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include "argtable3.h" + +// On Windows isspace crashes app in case of using Unicode character set and string to be above ASCII +// so you have to use _istspace instead of space +#ifdef UNICODE +#include + #define ISSPACE _istspace +#else + #define ISSPACE isspace +#endif + +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 2013 Tom G. Huang + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef ARG_UTILS_H +#define ARG_UTILS_H + +#define ARG_ENABLE_TRACE 0 +#define ARG_ENABLE_LOG 1 + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + EMINCOUNT = 1, + EMAXCOUNT, + EBADINT, + // The same name define EOVERFLOW in errno.h on windows platform +#ifdef __STDC_WANT_SECURE_LIB__ + EOVERFLOW_, +#else + EOVERFLOW, +#endif + EBADDOUBLE, + EBADDATE, + EREGNOMATCH +}; + + +#if defined(_MSC_VER) +#define ARG_TRACE(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0) \ + __pragma(warning(pop)) + +#define ARG_LOG(x) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0) \ + __pragma(warning(pop)) +#else +#define ARG_TRACE(x) \ + do { if (ARG_ENABLE_TRACE) dbg_printf x; } while (0) + +#define ARG_LOG(x) \ + do { if (ARG_ENABLE_LOG) dbg_printf x; } while (0) +#endif + +extern void dbg_printf(const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif + +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + + +void dbg_printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ +/* $FreeBSD$ */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD + * + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#include + +/* + * GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension. + * getopt() is declared here too for GNU programs. + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +__BEGIN_DECLS +int getopt_long(int, char * const *, const char *, + const struct option *, int *); +int getopt_long_only(int, char * const *, const char *, + const struct option *, int *); +#ifndef _GETOPT_DECLARED +#define _GETOPT_DECLARED +int getopt(int, char * const [], const char *); + +extern char *optarg; /* getopt(3) external variables */ +extern int optind, opterr, optopt; +#endif +#ifndef _OPTRESET_DECLARED +#define _OPTRESET_DECLARED +extern int optreset; /* getopt(3) external variable */ +#endif +__END_DECLS + +#endif /* !_GETOPT_H_ */ +/* $Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */ +/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +// $Id: getopt_long.c,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $" + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if 0 +#include +#endif +#include +#include +#include + + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + + + +#ifdef _WIN32 + +/* Windows needs warnx(). We change the definition though: + * 1. (another) global is defined, opterrmsg, which holds the error message + * 2. errors are always printed out on stderr w/o the program name + * Note that opterrmsg always gets set no matter what opterr is set to. The + * error message will not be printed if opterr is 0 as usual. + */ + +#include +#include + +#define MAX_OPTER_MSG_SIZE 128 + +extern char opterrmsg[MAX_OPTER_MSG_SIZE]; +char opterrmsg[MAX_OPTER_MSG_SIZE]; /* buffer for the last error message */ + +static void warnx(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + /* + Make sure opterrmsg is always zero-terminated despite the _vsnprintf() + implementation specifics and manually suppress the warning. + */ + memset(opterrmsg, 0, sizeof opterrmsg); + if (fmt != NULL) +#ifdef __STDC_WANT_SECURE_LIB__ + _vsnprintf_s(opterrmsg, MAX_OPTER_MSG_SIZE, sizeof(opterrmsg) - 1, fmt, ap); +#else + _vsnprintf(opterrmsg, sizeof(opterrmsg) - 1, fmt, ap); +#endif + va_end(ap); + +#pragma warning(suppress: 6053) + fprintf(stderr, "%s\n", opterrmsg); +} + +#else +#include +#endif /*_WIN32*/ + + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = place; + match = -1; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; +#ifdef __STDC_WANT_SECURE_LIB__ + char* buffer = NULL; + size_t buffer_size = 0; + errno_t err = 0; +#endif + + if (options == NULL) + return (-1); + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + +#ifdef __STDC_WANT_SECURE_LIB__ + if (posixly_correct == -1) { + err = _dupenv_s(&buffer, &buffer_size, "POSIXLY_CORRECT") == 0; + posixly_correct = buffer != NULL; + if(buffer != NULL && err == 0) { + free(buffer); + } + } +#else + if (posixly_correct == -1) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); +#endif + if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + else if (*options == '-') + flags |= FLAG_ALLARGS; + if (*options == '+' || *options == '-') + options++; + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +#include "argtable3.h" + + +char * arg_strptime(const char *buf, const char *fmt, struct tm *tm); + + +static void arg_date_resetfn(struct arg_date *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +static int arg_date_scanfn(struct arg_date *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* no argument value was given, leave parent->tmval[] unaltered but still count it */ + parent->count++; + } + else + { + const char *pend; + struct tm tm = parent->tmval[parent->count]; + + /* parse the given argument value, store result in parent->tmval[] */ + pend = arg_strptime(argval, parent->format, &tm); + if (pend && pend[0] == '\0') + parent->tmval[parent->count++] = tm; + else + errorcode = EBADDATE; + } + + ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static int arg_date_checkfn(struct arg_date *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_date_errorfn( + struct arg_date *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + case EBADDATE: + { + struct tm tm; + char buff[200]; + + fprintf(fp, "illegal timestamp format \"%s\"\n", argval); + memset(&tm, 0, sizeof(tm)); + arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm); + strftime(buff, sizeof(buff), parent->format, &tm); + printf("correct format is \"%s\"\n", buff); + break; + } + } +} + + +struct arg_date * arg_date0( + const char * shortopts, + const char * longopts, + const char * format, + const char *datatype, + const char *glossary) +{ + return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary); +} + + +struct arg_date * arg_date1( + const char * shortopts, + const char * longopts, + const char * format, + const char *datatype, + const char *glossary) +{ + return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary); +} + + +struct arg_date * arg_daten( + const char * shortopts, + const char * longopts, + const char * format, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_date *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + /* default time format is the national date format for the locale */ + if (!format) + format = "%x"; + + nbytes = sizeof(struct arg_date) /* storage for struct arg_date */ + + maxcount * sizeof(struct tm); /* storage for tmval[maxcount] array */ + + /* allocate storage for the arg_date struct + tmval[] array. */ + /* we use calloc because we want the tmval[] array zero filled. */ + result = (struct arg_date *)calloc(1, nbytes); + if (result) + { + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : format; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_date_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_date_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_date_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_date_errorfn; + + /* store the tmval[maxcount] array immediately after the arg_date struct */ + result->tmval = (struct tm *)(result + 1); + + /* init the remaining arg_date member variables */ + result->count = 0; + result->format = format; + } + + ARG_TRACE(("arg_daten() returns %p\n", result)); + return result; +} + + +/*- + * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * Heavily optimised by David Laight + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +/* + * We do not implement alternate representations. However, we always + * check whether a given modifier is allowed for a certain conversion. + */ +#define ALT_E 0x01 +#define ALT_O 0x02 +#define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } +#define TM_YEAR_BASE (1900) + +static int conv_num(const char * *, int *, int, int); + +static const char *day[7] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday" +}; + +static const char *abday[7] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +static const char *mon[12] = { + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December" +}; + +static const char *abmon[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static const char *am_pm[2] = { + "AM", "PM" +}; + + +static int arg_strcasecmp(const char *s1, const char *s2) +{ + const unsigned char *us1 = (const unsigned char *)s1; + const unsigned char *us2 = (const unsigned char *)s2; + while (tolower(*us1) == tolower(*us2++)) + if (*us1++ == '\0') + return 0; + + return tolower(*us1) - tolower(*--us2); +} + + +static int arg_strncasecmp(const char *s1, const char *s2, size_t n) +{ + if (n != 0) + { + const unsigned char *us1 = (const unsigned char *)s1; + const unsigned char *us2 = (const unsigned char *)s2; + do + { + if (tolower(*us1) != tolower(*us2++)) + return tolower(*us1) - tolower(*--us2); + + if (*us1++ == '\0') + break; + } while (--n != 0); + } + + return 0; +} + + +char * arg_strptime(const char *buf, const char *fmt, struct tm *tm) +{ + char c; + const char *bp; + size_t len = 0; + int alt_format, i, split_year = 0; + + bp = buf; + + while ((c = *fmt) != '\0') { + /* Clear `alternate' modifier prior to new conversion. */ + alt_format = 0; + + /* Eat up white-space. */ + if (ISSPACE(c)) { + while (ISSPACE(*bp)) + bp++; + + fmt++; + continue; + } + + if ((c = *fmt++) != '%') + goto literal; + + +again: + switch (c = *fmt++) + { + case '%': /* "%%" is converted to "%". */ +literal: + if (c != *bp++) + return (0); + break; + + /* + * "Alternative" modifiers. Just set the appropriate flag + * and start over again. + */ + case 'E': /* "%E?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_E; + goto again; + + case 'O': /* "%O?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_O; + goto again; + + /* + * "Complex" conversion rules, implemented through recursion. + */ + case 'c': /* Date and time, using the locale's format. */ + LEGAL_ALT(ALT_E); + bp = arg_strptime(bp, "%x %X", tm); + if (!bp) + return (0); + break; + + case 'D': /* The date as "%m/%d/%y". */ + LEGAL_ALT(0); + bp = arg_strptime(bp, "%m/%d/%y", tm); + if (!bp) + return (0); + break; + + case 'R': /* The time as "%H:%M". */ + LEGAL_ALT(0); + bp = arg_strptime(bp, "%H:%M", tm); + if (!bp) + return (0); + break; + + case 'r': /* The time in 12-hour clock representation. */ + LEGAL_ALT(0); + bp = arg_strptime(bp, "%I:%M:%S %p", tm); + if (!bp) + return (0); + break; + + case 'T': /* The time as "%H:%M:%S". */ + LEGAL_ALT(0); + bp = arg_strptime(bp, "%H:%M:%S", tm); + if (!bp) + return (0); + break; + + case 'X': /* The time, using the locale's format. */ + LEGAL_ALT(ALT_E); + bp = arg_strptime(bp, "%H:%M:%S", tm); + if (!bp) + return (0); + break; + + case 'x': /* The date, using the locale's format. */ + LEGAL_ALT(ALT_E); + bp = arg_strptime(bp, "%m/%d/%y", tm); + if (!bp) + return (0); + break; + + /* + * "Elementary" conversion rules. + */ + case 'A': /* The day of week, using the locale's form. */ + case 'a': + LEGAL_ALT(0); + for (i = 0; i < 7; i++) { + /* Full name. */ + len = strlen(day[i]); + if (arg_strncasecmp(day[i], bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(abday[i]); + if (arg_strncasecmp(abday[i], bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 7) + return (0); + + tm->tm_wday = i; + bp += len; + break; + + case 'B': /* The month, using the locale's form. */ + case 'b': + case 'h': + LEGAL_ALT(0); + for (i = 0; i < 12; i++) { + /* Full name. */ + len = strlen(mon[i]); + if (arg_strncasecmp(mon[i], bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(abmon[i]); + if (arg_strncasecmp(abmon[i], bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 12) + return (0); + + tm->tm_mon = i; + bp += len; + break; + + case 'C': /* The century number. */ + LEGAL_ALT(ALT_E); + if (!(conv_num(&bp, &i, 0, 99))) + return (0); + + if (split_year) { + tm->tm_year = (tm->tm_year % 100) + (i * 100); + } else { + tm->tm_year = i * 100; + split_year = 1; + } + break; + + case 'd': /* The day of month. */ + case 'e': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_mday, 1, 31))) + return (0); + break; + + case 'k': /* The hour (24-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'H': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_hour, 0, 23))) + return (0); + break; + + case 'l': /* The hour (12-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'I': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_hour, 1, 12))) + return (0); + if (tm->tm_hour == 12) + tm->tm_hour = 0; + break; + + case 'j': /* The day of year. */ + LEGAL_ALT(0); + if (!(conv_num(&bp, &i, 1, 366))) + return (0); + tm->tm_yday = i - 1; + break; + + case 'M': /* The minute. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_min, 0, 59))) + return (0); + break; + + case 'm': /* The month. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &i, 1, 12))) + return (0); + tm->tm_mon = i - 1; + break; + + case 'p': /* The locale's equivalent of AM/PM. */ + LEGAL_ALT(0); + /* AM? */ + if (arg_strcasecmp(am_pm[0], bp) == 0) { + if (tm->tm_hour > 11) + return (0); + + bp += strlen(am_pm[0]); + break; + } + /* PM? */ + else if (arg_strcasecmp(am_pm[1], bp) == 0) { + if (tm->tm_hour > 11) + return (0); + + tm->tm_hour += 12; + bp += strlen(am_pm[1]); + break; + } + + /* Nothing matched. */ + return (0); + + case 'S': /* The seconds. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_sec, 0, 61))) + return (0); + break; + + case 'U': /* The week of year, beginning on sunday. */ + case 'W': /* The week of year, beginning on monday. */ + LEGAL_ALT(ALT_O); + /* + * XXX This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so just check the + * range for now. + */ + if (!(conv_num(&bp, &i, 0, 53))) + return (0); + break; + + case 'w': /* The day of week, beginning on sunday. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_wday, 0, 6))) + return (0); + break; + + case 'Y': /* The year. */ + LEGAL_ALT(ALT_E); + if (!(conv_num(&bp, &i, 0, 9999))) + return (0); + + tm->tm_year = i - TM_YEAR_BASE; + break; + + case 'y': /* The year within 100 years of the epoch. */ + LEGAL_ALT(ALT_E | ALT_O); + if (!(conv_num(&bp, &i, 0, 99))) + return (0); + + if (split_year) { + tm->tm_year = ((tm->tm_year / 100) * 100) + i; + break; + } + split_year = 1; + if (i <= 68) + tm->tm_year = i + 2000 - TM_YEAR_BASE; + else + tm->tm_year = i + 1900 - TM_YEAR_BASE; + break; + + /* + * Miscellaneous conversions. + */ + case 'n': /* Any kind of white-space. */ + case 't': + LEGAL_ALT(0); + while (ISSPACE(*bp)) + bp++; + break; + + + default: /* Unknown/unsupported conversion. */ + return (0); + } + + + } + + /* LINTED functional specification */ + return ((char *)bp); +} + + +static int conv_num(const char * *buf, int *dest, int llim, int ulim) +{ + int result = 0; + + /* The limit also determines the number of valid digits. */ + int rulim = ulim; + + if (**buf < '0' || **buf > '9') + return (0); + + do { + result *= 10; + result += *(*buf)++ - '0'; + rulim /= 10; + } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); + + if (result < llim || result > ulim) + return (0); + + *dest = result; + return (1); +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + + +static void arg_dbl_resetfn(struct arg_dbl *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +static int arg_dbl_scanfn(struct arg_dbl *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent argument value unaltered but still count the argument. */ + parent->count++; + } + else + { + double val; + char *end; + + /* extract double from argval into val */ + val = strtod(argval, &end); + + /* if success then store result in parent->dval[] array otherwise return error*/ + if (*end == 0) + parent->dval[parent->count++] = val; + else + errorcode = EBADDOUBLE; + } + + ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static int arg_dbl_checkfn(struct arg_dbl *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_dbl_errorfn( + struct arg_dbl *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + case EBADDOUBLE: + fprintf(fp, "invalid argument \"%s\" to option ", argval); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + } +} + + +struct arg_dbl * arg_dbl0( + const char * shortopts, + const char * longopts, + const char *datatype, + const char *glossary) +{ + return arg_dbln(shortopts, longopts, datatype, 0, 1, glossary); +} + + +struct arg_dbl * arg_dbl1( + const char * shortopts, + const char * longopts, + const char *datatype, + const char *glossary) +{ + return arg_dbln(shortopts, longopts, datatype, 1, 1, glossary); +} + + +struct arg_dbl * arg_dbln( + const char * shortopts, + const char * longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_dbl *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_dbl) /* storage for struct arg_dbl */ + + (maxcount + 1) * sizeof(double); /* storage for dval[maxcount] array plus one extra for padding to memory boundary */ + + result = (struct arg_dbl *)malloc(nbytes); + if (result) + { + size_t addr; + size_t rem; + + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : ""; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_dbl_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_dbl_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_dbl_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_dbl_errorfn; + + /* Store the dval[maxcount] array on the first double boundary that + * immediately follows the arg_dbl struct. We do the memory alignment + * purely for SPARC and Motorola systems. They require floats and + * doubles to be aligned on natural boundaries. + */ + addr = (size_t)(result + 1); + rem = addr % sizeof(double); + result->dval = (double *)(addr + sizeof(double) - rem); + ARG_TRACE(("addr=%p, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem)); + + result->count = 0; + } + + ARG_TRACE(("arg_dbln() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + + +static void arg_end_resetfn(struct arg_end *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + +static void arg_end_errorfn( + void *parent, + FILE *fp, + int error, + const char *argval, + const char *progname) +{ + /* suppress unreferenced formal parameter warning */ + (void)parent; + + progname = progname ? progname : ""; + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(error) + { + case ARG_ELIMIT: + fputs("too many errors to display", fp); + break; + case ARG_EMALLOC: + fputs("insufficent memory", fp); + break; + case ARG_ENOMATCH: + fprintf(fp, "unexpected argument \"%s\"", argval); + break; + case ARG_EMISSARG: + fprintf(fp, "option \"%s\" requires an argument", argval); + break; + case ARG_ELONGOPT: + fprintf(fp, "invalid option \"%s\"", argval); + break; + default: + fprintf(fp, "invalid option \"-%c\"", error); + break; + } + + fputc('\n', fp); +} + + +struct arg_end * arg_end(int maxcount) +{ + size_t nbytes; + struct arg_end *result; + + nbytes = sizeof(struct arg_end) + + maxcount * sizeof(int) /* storage for int error[maxcount] array*/ + + maxcount * sizeof(void *) /* storage for void* parent[maxcount] array */ + + maxcount * sizeof(char *); /* storage for char* argval[maxcount] array */ + + result = (struct arg_end *)malloc(nbytes); + if (result) + { + /* init the arg_hdr struct */ + result->hdr.flag = ARG_TERMINATOR; + result->hdr.shortopts = NULL; + result->hdr.longopts = NULL; + result->hdr.datatype = NULL; + result->hdr.glossary = NULL; + result->hdr.mincount = 1; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_end_resetfn; + result->hdr.scanfn = NULL; + result->hdr.checkfn = NULL; + result->hdr.errorfn = (arg_errorfn *)arg_end_errorfn; + + /* store error[maxcount] array immediately after struct arg_end */ + result->error = (int *)(result + 1); + + /* store parent[maxcount] array immediately after error[] array */ + result->parent = (void * *)(result->error + maxcount ); + + /* store argval[maxcount] array immediately after parent[] array */ + result->argval = (const char * *)(result->parent + maxcount ); + } + + ARG_TRACE(("arg_end(%d) returns %p\n", maxcount, result)); + return result; +} + + +void arg_print_errors(FILE * fp, struct arg_end * end, const char * progname) +{ + int i; + ARG_TRACE(("arg_errors()\n")); + for (i = 0; i < end->count; i++) + { + struct arg_hdr *errorparent = (struct arg_hdr *)(end->parent[i]); + if (errorparent->errorfn) + errorparent->errorfn(end->parent[i], + fp, + end->error[i], + end->argval[i], + progname); + } +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +#include "argtable3.h" + +#ifdef WIN32 +# define FILESEPARATOR1 '\\' +# define FILESEPARATOR2 '/' +#else +# define FILESEPARATOR1 '/' +# define FILESEPARATOR2 '/' +#endif + + +static void arg_file_resetfn(struct arg_file *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +/* Returns ptr to the base filename within *filename */ +static const char * arg_basename(const char *filename) +{ + const char *result = NULL, *result1, *result2; + + /* Find the last occurrence of eother file separator character. */ + /* Two alternative file separator chars are supported as legal */ + /* file separators but not both together in the same filename. */ + result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL); + result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL); + + if (result2) + result = result2 + 1; /* using FILESEPARATOR2 (the alternative file separator) */ + + if (result1) + result = result1 + 1; /* using FILESEPARATOR1 (the preferred file separator) */ + + if (!result) + result = filename; /* neither file separator was found so basename is the whole filename */ + + /* special cases of "." and ".." are not considered basenames */ + if (result && ( strcmp(".", result) == 0 || strcmp("..", result) == 0 )) + result = filename + strlen(filename); + + return result; +} + + +/* Returns ptr to the file extension within *basename */ +static const char * arg_extension(const char *basename) +{ + /* find the last occurrence of '.' in basename */ + const char *result = (basename ? strrchr(basename, '.') : NULL); + + /* if no '.' was found then return pointer to end of basename */ + if (basename && !result) + result = basename + strlen(basename); + + /* special case: basenames with a single leading dot (eg ".foo") are not considered as true extensions */ + if (basename && result == basename) + result = basename + strlen(basename); + + /* special case: empty extensions (eg "foo.","foo..") are not considered as true extensions */ + if (basename && result && result[1] == '\0') + result = basename + strlen(basename); + + return result; +} + + +static int arg_file_scanfn(struct arg_file *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent arguiment value unaltered but still count the argument. */ + parent->count++; + } + else + { + parent->filename[parent->count] = argval; + parent->basename[parent->count] = arg_basename(argval); + parent->extension[parent->count] = + arg_extension(parent->basename[parent->count]); /* only seek extensions within the basename (not the file path)*/ + parent->count++; + } + + ARG_TRACE(("%s4:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static int arg_file_checkfn(struct arg_file *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_file_errorfn( + struct arg_file *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + default: + fprintf(fp, "unknown error at \"%s\"\n", argval); + } +} + + +struct arg_file * arg_file0( + const char * shortopts, + const char * longopts, + const char *datatype, + const char *glossary) +{ + return arg_filen(shortopts, longopts, datatype, 0, 1, glossary); +} + + +struct arg_file * arg_file1( + const char * shortopts, + const char * longopts, + const char *datatype, + const char *glossary) +{ + return arg_filen(shortopts, longopts, datatype, 1, 1, glossary); +} + + +struct arg_file * arg_filen( + const char * shortopts, + const char * longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_file *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_file) /* storage for struct arg_file */ + + sizeof(char *) * maxcount /* storage for filename[maxcount] array */ + + sizeof(char *) * maxcount /* storage for basename[maxcount] array */ + + sizeof(char *) * maxcount; /* storage for extension[maxcount] array */ + + result = (struct arg_file *)malloc(nbytes); + if (result) + { + int i; + + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.glossary = glossary; + result->hdr.datatype = datatype ? datatype : ""; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_file_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_file_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_file_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_file_errorfn; + + /* store the filename,basename,extension arrays immediately after the arg_file struct */ + result->filename = (const char * *)(result + 1); + result->basename = result->filename + maxcount; + result->extension = result->basename + maxcount; + result->count = 0; + + /* foolproof the string pointers by initialising them with empty strings */ + for (i = 0; i < maxcount; i++) + { + result->filename[i] = ""; + result->basename[i] = ""; + result->extension[i] = ""; + } + } + + ARG_TRACE(("arg_filen() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include + +#include "argtable3.h" + + +static void arg_int_resetfn(struct arg_int *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +/* strtol0x() is like strtol() except that the numeric string is */ +/* expected to be prefixed by "0X" where X is a user supplied char. */ +/* The string may optionally be prefixed by white space and + or - */ +/* as in +0X123 or -0X123. */ +/* Once the prefix has been scanned, the remainder of the numeric */ +/* string is converted using strtol() with the given base. */ +/* eg: to parse hex str="-0X12324", specify X='X' and base=16. */ +/* eg: to parse oct str="+0o12324", specify X='O' and base=8. */ +/* eg: to parse bin str="-0B01010", specify X='B' and base=2. */ +/* Failure of conversion is indicated by result where *endptr==str. */ +static long int strtol0X(const char * str, + const char * *endptr, + char X, + int base) +{ + long int val; /* stores result */ + int s = 1; /* sign is +1 or -1 */ + const char *ptr = str; /* ptr to current position in str */ + + /* skip leading whitespace */ + while (ISSPACE(*ptr)) + ptr++; + /* printf("1) %s\n",ptr); */ + + /* scan optional sign character */ + switch (*ptr) + { + case '+': + ptr++; + s = 1; + break; + case '-': + ptr++; + s = -1; + break; + default: + s = 1; + break; + } + /* printf("2) %s\n",ptr); */ + + /* '0X' prefix */ + if ((*ptr++) != '0') + { + /* printf("failed to detect '0'\n"); */ + *endptr = str; + return 0; + } + /* printf("3) %s\n",ptr); */ + if (toupper(*ptr++) != toupper(X)) + { + /* printf("failed to detect '%c'\n",X); */ + *endptr = str; + return 0; + } + /* printf("4) %s\n",ptr); */ + + /* attempt conversion on remainder of string using strtol() */ + val = strtol(ptr, (char * *)endptr, base); + if (*endptr == ptr) + { + /* conversion failed */ + *endptr = str; + return 0; + } + + /* success */ + return s * val; +} + + +/* Returns 1 if str matches suffix (case insensitive). */ +/* Str may contain trailing whitespace, but nothing else. */ +static int detectsuffix(const char *str, const char *suffix) +{ + /* scan pairwise through strings until mismatch detected */ + while( toupper(*str) == toupper(*suffix) ) + { + /* printf("'%c' '%c'\n", *str, *suffix); */ + + /* return 1 (success) if match persists until the string terminator */ + if (*str == '\0') + return 1; + + /* next chars */ + str++; + suffix++; + } + /* printf("'%c' '%c' mismatch\n", *str, *suffix); */ + + /* return 0 (fail) if the matching did not consume the entire suffix */ + if (*suffix != 0) + return 0; /* failed to consume entire suffix */ + + /* skip any remaining whitespace in str */ + while (ISSPACE(*str)) + str++; + + /* return 1 (success) if we have reached end of str else return 0 (fail) */ + return (*str == '\0') ? 1 : 0; +} + + +static int arg_int_scanfn(struct arg_int *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent arguiment value unaltered but still count the argument. */ + parent->count++; + } + else + { + long int val; + const char *end; + + /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */ + val = strtol0X(argval, &end, 'X', 16); + if (end == argval) + { + /* hex failed, attempt octal conversion (eg +0o123) */ + val = strtol0X(argval, &end, 'O', 8); + if (end == argval) + { + /* octal failed, attempt binary conversion (eg +0B101) */ + val = strtol0X(argval, &end, 'B', 2); + if (end == argval) + { + /* binary failed, attempt decimal conversion with no prefix (eg 1234) */ + val = strtol(argval, (char * *)&end, 10); + if (end == argval) + { + /* all supported number formats failed */ + return EBADINT; + } + } + } + } + + /* Safety check for integer overflow. WARNING: this check */ + /* achieves nothing on machines where size(int)==size(long). */ + if ( val > INT_MAX || val < INT_MIN ) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; +#else + errorcode = EOVERFLOW; +#endif + + /* Detect any suffixes (KB,MB,GB) and multiply argument value appropriately. */ + /* We need to be mindful of integer overflows when using such big numbers. */ + if (detectsuffix(end, "KB")) /* kilobytes */ + { + if ( val > (INT_MAX / 1024) || val < (INT_MIN / 1024) ) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */ +#else + errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ +#endif + else + val *= 1024; /* 1KB = 1024 */ + } + else if (detectsuffix(end, "MB")) /* megabytes */ + { + if ( val > (INT_MAX / 1048576) || val < (INT_MIN / 1048576) ) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */ +#else + errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ +#endif + else + val *= 1048576; /* 1MB = 1024*1024 */ + } + else if (detectsuffix(end, "GB")) /* gigabytes */ + { + if ( val > (INT_MAX / 1073741824) || val < (INT_MIN / 1073741824) ) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */ +#else + errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ +#endif + else + val *= 1073741824; /* 1GB = 1024*1024*1024 */ + } + else if (!detectsuffix(end, "")) + errorcode = EBADINT; /* invalid suffix detected */ + + /* if success then store result in parent->ival[] array */ + if (errorcode == 0) + parent->ival[parent->count++] = val; + } + + /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */ + return errorcode; +} + + +static int arg_int_checkfn(struct arg_int *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ + return errorcode; +} + + +static void arg_int_errorfn( + struct arg_int *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + case EBADINT: + fprintf(fp, "invalid argument \"%s\" to option ", argval); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + +#ifdef __STDC_WANT_SECURE_LIB__ + case EOVERFLOW_: +#else + case EOVERFLOW: +#endif + fputs("integer overflow at option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, " "); + fprintf(fp, "(%s is too large)\n", argval); + break; + } +} + + +struct arg_int * arg_int0( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) +{ + return arg_intn(shortopts, longopts, datatype, 0, 1, glossary); +} + + +struct arg_int * arg_int1( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) +{ + return arg_intn(shortopts, longopts, datatype, 1, 1, glossary); +} + + +struct arg_int * arg_intn( + const char *shortopts, + const char *longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_int *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_int) /* storage for struct arg_int */ + + maxcount * sizeof(int); /* storage for ival[maxcount] array */ + + result = (struct arg_int *)malloc(nbytes); + if (result) + { + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : ""; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_int_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_int_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_int_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_int_errorfn; + + /* store the ival[maxcount] array immediately after the arg_int struct */ + result->ival = (int *)(result + 1); + result->count = 0; + } + + ARG_TRACE(("arg_intn() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + + +static void arg_lit_resetfn(struct arg_lit *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +static int arg_lit_scanfn(struct arg_lit *parent, const char *argval) +{ + int errorcode = 0; + if (parent->count < parent->hdr.maxcount ) + parent->count++; + else + errorcode = EMAXCOUNT; + + ARG_TRACE(("%s:scanfn(%p,%s) returns %d\n", __FILE__, parent, argval, + errorcode)); + return errorcode; +} + + +static int arg_lit_checkfn(struct arg_lit *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_lit_errorfn( + struct arg_lit *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + switch(errorcode) + { + case EMINCOUNT: + fprintf(fp, "%s: missing option ", progname); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + fprintf(fp, "\n"); + break; + + case EMAXCOUNT: + fprintf(fp, "%s: extraneous option ", progname); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + } + + ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, fp, + errorcode, argval, progname)); +} + + +struct arg_lit * arg_lit0( + const char * shortopts, + const char * longopts, + const char * glossary) +{ + return arg_litn(shortopts, longopts, 0, 1, glossary); +} + + +struct arg_lit * arg_lit1( + const char *shortopts, + const char *longopts, + const char *glossary) +{ + return arg_litn(shortopts, longopts, 1, 1, glossary); +} + + +struct arg_lit * arg_litn( + const char *shortopts, + const char *longopts, + int mincount, + int maxcount, + const char *glossary) +{ + struct arg_lit *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + result = (struct arg_lit *)malloc(sizeof(struct arg_lit)); + if (result) + { + /* init the arg_hdr struct */ + result->hdr.flag = 0; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = NULL; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_lit_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_lit_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_lit_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_lit_errorfn; + + /* init local variables */ + result->count = 0; + } + + ARG_TRACE(("arg_litn() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + +struct arg_rem *arg_rem(const char *datatype, const char *glossary) +{ + struct arg_rem *result = (struct arg_rem *)malloc(sizeof(struct arg_rem)); + if (result) + { + result->hdr.flag = 0; + result->hdr.shortopts = NULL; + result->hdr.longopts = NULL; + result->hdr.datatype = datatype; + result->hdr.glossary = glossary; + result->hdr.mincount = 1; + result->hdr.maxcount = 1; + result->hdr.parent = result; + result->hdr.resetfn = NULL; + result->hdr.scanfn = NULL; + result->hdr.checkfn = NULL; + result->hdr.errorfn = NULL; + } + + ARG_TRACE(("arg_rem() returns %p\n", result)); + return result; +} + +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include + +#include "argtable3.h" + + +#ifndef _TREX_H_ +#define _TREX_H_ +/*************************************************************** + T-Rex a tiny regular expression library + + Copyright (C) 2003-2006 Alberto Demichelis + + This software is provided 'as-is', without any express + or implied warranty. In no event will the authors be held + liable for any damages arising from the use of this software. + + Permission is granted to anyone to use this software for + any purpose, including commercial applications, and to alter + it and redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but + is not required. + + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any + source distribution. + +****************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _UNICODE +#define TRexChar unsigned short +#define MAX_CHAR 0xFFFF +#define _TREXC(c) L##c +#define trex_strlen wcslen +#define trex_printf wprintf +#else +#define TRexChar char +#define MAX_CHAR 0xFF +#define _TREXC(c) (c) +#define trex_strlen strlen +#define trex_printf printf +#endif + +#ifndef TREX_API +#define TREX_API extern +#endif + +#define TRex_True 1 +#define TRex_False 0 + +#define TREX_ICASE ARG_REX_ICASE + +typedef unsigned int TRexBool; +typedef struct TRex TRex; + +typedef struct { + const TRexChar *begin; + int len; +} TRexMatch; + +TREX_API TRex *trex_compile(const TRexChar *pattern, const TRexChar **error, int flags); +TREX_API void trex_free(TRex *exp); +TREX_API TRexBool trex_match(TRex* exp, const TRexChar* text); +TREX_API TRexBool trex_search(TRex* exp, const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end); +TREX_API TRexBool trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end); +TREX_API int trex_getsubexpcount(TRex* exp); +TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp); + +#ifdef __cplusplus +} +#endif + +#endif + + + +struct privhdr +{ + const char *pattern; + int flags; +}; + + +static void arg_rex_resetfn(struct arg_rex *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + +static int arg_rex_scanfn(struct arg_rex *parent, const char *argval) +{ + int errorcode = 0; + const TRexChar *error = NULL; + TRex *rex = NULL; + TRexBool is_match = TRex_False; + + if (parent->count == parent->hdr.maxcount ) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent argument value unaltered but still count the argument. */ + parent->count++; + } + else + { + struct privhdr *priv = (struct privhdr *)parent->hdr.priv; + + /* test the current argument value for a match with the regular expression */ + /* if a match is detected, record the argument value in the arg_rex struct */ + + rex = trex_compile(priv->pattern, &error, priv->flags); + is_match = trex_match(rex, argval); + if (!is_match) + errorcode = EREGNOMATCH; + else + parent->sval[parent->count++] = argval; + + trex_free(rex); + } + + ARG_TRACE(("%s:scanfn(%p) returns %d\n",__FILE__,parent,errorcode)); + return errorcode; +} + +static int arg_rex_checkfn(struct arg_rex *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + //struct privhdr *priv = (struct privhdr*)parent->hdr.priv; + + /* free the regex "program" we constructed in resetfn */ + //regfree(&(priv->regex)); + + /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ + return errorcode; +} + +static void arg_rex_errorfn(struct arg_rex *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + case EREGNOMATCH: + fputs("illegal value ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + default: + { + //char errbuff[256]; + //regerror(errorcode, NULL, errbuff, sizeof(errbuff)); + //printf("%s\n", errbuff); + } + break; + } +} + + +struct arg_rex * arg_rex0(const char * shortopts, + const char * longopts, + const char * pattern, + const char *datatype, + int flags, + const char *glossary) +{ + return arg_rexn(shortopts, + longopts, + pattern, + datatype, + 0, + 1, + flags, + glossary); +} + +struct arg_rex * arg_rex1(const char * shortopts, + const char * longopts, + const char * pattern, + const char *datatype, + int flags, + const char *glossary) +{ + return arg_rexn(shortopts, + longopts, + pattern, + datatype, + 1, + 1, + flags, + glossary); +} + + +struct arg_rex * arg_rexn(const char * shortopts, + const char * longopts, + const char * pattern, + const char *datatype, + int mincount, + int maxcount, + int flags, + const char *glossary) +{ + size_t nbytes; + struct arg_rex *result; + struct privhdr *priv; + int i; + const TRexChar *error = NULL; + TRex *rex = NULL; + + if (!pattern) + { + printf( + "argtable: ERROR - illegal regular expression pattern \"(NULL)\"\n"); + printf("argtable: Bad argument table.\n"); + return NULL; + } + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_rex) /* storage for struct arg_rex */ + + sizeof(struct privhdr) /* storage for private arg_rex data */ + + maxcount * sizeof(char *); /* storage for sval[maxcount] array */ + + result = (struct arg_rex *)malloc(nbytes); + if (result == NULL) + return result; + + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : pattern; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_rex_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_rex_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_rex_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_rex_errorfn; + + /* store the arg_rex_priv struct immediately after the arg_rex struct */ + result->hdr.priv = result + 1; + priv = (struct privhdr *)(result->hdr.priv); + priv->pattern = pattern; + priv->flags = flags; + + /* store the sval[maxcount] array immediately after the arg_rex_priv struct */ + result->sval = (const char * *)(priv + 1); + result->count = 0; + + /* foolproof the string pointers by initializing them to reference empty strings */ + for (i = 0; i < maxcount; i++) + result->sval[i] = ""; + + /* here we construct and destroy a regex representation of the regular + * expression for no other reason than to force any regex errors to be + * trapped now rather than later. If we don't, then errors may go undetected + * until an argument is actually parsed. + */ + + rex = trex_compile(priv->pattern, &error, priv->flags); + if (rex == NULL) + { + ARG_LOG(("argtable: %s \"%s\"\n", error ? error : _TREXC("undefined"), priv->pattern)); + ARG_LOG(("argtable: Bad argument table.\n")); + } + + trex_free(rex); + + ARG_TRACE(("arg_rexn() returns %p\n", result)); + return result; +} + + + +/* see copyright notice in trex.h */ +#include +#include +#include +#include + +#ifdef _UINCODE +#define scisprint iswprint +#define scstrlen wcslen +#define scprintf wprintf +#define _SC(x) L(x) +#else +#define scisprint isprint +#define scstrlen strlen +#define scprintf printf +#define _SC(x) (x) +#endif + +#ifdef _DEBUG +#include + +static const TRexChar *g_nnames[] = +{ + _SC("NONE"),_SC("OP_GREEDY"), _SC("OP_OR"), + _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"), _SC("OP_CLASS"), + _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"), + _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB") +}; + +#endif +#define OP_GREEDY (MAX_CHAR+1) // * + ? {n} +#define OP_OR (MAX_CHAR+2) +#define OP_EXPR (MAX_CHAR+3) //parentesis () +#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:) +#define OP_DOT (MAX_CHAR+5) +#define OP_CLASS (MAX_CHAR+6) +#define OP_CCLASS (MAX_CHAR+7) +#define OP_NCLASS (MAX_CHAR+8) //negates class the [^ +#define OP_RANGE (MAX_CHAR+9) +#define OP_CHAR (MAX_CHAR+10) +#define OP_EOL (MAX_CHAR+11) +#define OP_BOL (MAX_CHAR+12) +#define OP_WB (MAX_CHAR+13) + +#define TREX_SYMBOL_ANY_CHAR ('.') +#define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+') +#define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*') +#define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?') +#define TREX_SYMBOL_BRANCH ('|') +#define TREX_SYMBOL_END_OF_STRING ('$') +#define TREX_SYMBOL_BEGINNING_OF_STRING ('^') +#define TREX_SYMBOL_ESCAPE_CHAR ('\\') + + +typedef int TRexNodeType; + +typedef struct tagTRexNode{ + TRexNodeType type; + int left; + int right; + int next; +}TRexNode; + +struct TRex{ + const TRexChar *_eol; + const TRexChar *_bol; + const TRexChar *_p; + int _first; + int _op; + TRexNode *_nodes; + int _nallocated; + int _nsize; + int _nsubexpr; + TRexMatch *_matches; + int _currsubexp; + void *_jmpbuf; + const TRexChar **_error; + int _flags; +}; + +static int trex_list(TRex *exp); + +static int trex_newnode(TRex *exp, TRexNodeType type) +{ + TRexNode n; + int newid; + n.type = type; + n.next = n.right = n.left = -1; + if(type == OP_EXPR) + n.right = exp->_nsubexpr++; + if(exp->_nallocated < (exp->_nsize + 1)) { + exp->_nallocated *= 2; + exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode)); + } + exp->_nodes[exp->_nsize++] = n; + newid = exp->_nsize - 1; + return (int)newid; +} + +static void trex_error(TRex *exp,const TRexChar *error) +{ + if(exp->_error) *exp->_error = error; + longjmp(*((jmp_buf*)exp->_jmpbuf),-1); +} + +static void trex_expect(TRex *exp, int n){ + if((*exp->_p) != n) + trex_error(exp, _SC("expected paren")); + exp->_p++; +} + +static TRexChar trex_escapechar(TRex *exp) +{ + if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){ + exp->_p++; + switch(*exp->_p) { + case 'v': exp->_p++; return '\v'; + case 'n': exp->_p++; return '\n'; + case 't': exp->_p++; return '\t'; + case 'r': exp->_p++; return '\r'; + case 'f': exp->_p++; return '\f'; + default: return (*exp->_p++); + } + } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected")); + return (*exp->_p++); +} + +static int trex_charclass(TRex *exp,int classid) +{ + int n = trex_newnode(exp,OP_CCLASS); + exp->_nodes[n].left = classid; + return n; +} + +static int trex_charnode(TRex *exp,TRexBool isclass) +{ + TRexChar t; + if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) { + exp->_p++; + switch(*exp->_p) { + case 'n': exp->_p++; return trex_newnode(exp,'\n'); + case 't': exp->_p++; return trex_newnode(exp,'\t'); + case 'r': exp->_p++; return trex_newnode(exp,'\r'); + case 'f': exp->_p++; return trex_newnode(exp,'\f'); + case 'v': exp->_p++; return trex_newnode(exp,'\v'); + case 'a': case 'A': case 'w': case 'W': case 's': case 'S': + case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': + case 'p': case 'P': case 'l': case 'u': + { + t = *exp->_p; exp->_p++; + return trex_charclass(exp,t); + } + case 'b': + case 'B': + if(!isclass) { + int node = trex_newnode(exp,OP_WB); + exp->_nodes[node].left = *exp->_p; + exp->_p++; + return node; + } //else default + default: + t = *exp->_p; exp->_p++; + return trex_newnode(exp,t); + } + } + else if(!scisprint(*exp->_p)) { + + trex_error(exp,_SC("letter expected")); + } + t = *exp->_p; exp->_p++; + return trex_newnode(exp,t); +} +static int trex_class(TRex *exp) +{ + int ret = -1; + int first = -1,chain; + if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){ + ret = trex_newnode(exp,OP_NCLASS); + exp->_p++; + }else ret = trex_newnode(exp,OP_CLASS); + + if(*exp->_p == ']') trex_error(exp,_SC("empty class")); + chain = ret; + while(*exp->_p != ']' && exp->_p != exp->_eol) { + if(*exp->_p == '-' && first != -1){ + int r,t; + if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range")); + r = trex_newnode(exp,OP_RANGE); + if(first>*exp->_p) trex_error(exp,_SC("invalid range")); + if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges")); + exp->_nodes[r].left = exp->_nodes[first].type; + t = trex_escapechar(exp); + exp->_nodes[r].right = t; + exp->_nodes[chain].next = r; + chain = r; + first = -1; + } + else{ + if(first!=-1){ + int c = first; + exp->_nodes[chain].next = c; + chain = c; + first = trex_charnode(exp,TRex_True); + } + else{ + first = trex_charnode(exp,TRex_True); + } + } + } + if(first!=-1){ + int c = first; + exp->_nodes[chain].next = c; + chain = c; + first = -1; + } + /* hack? */ + exp->_nodes[ret].left = exp->_nodes[ret].next; + exp->_nodes[ret].next = -1; + return ret; +} + +static int trex_parsenumber(TRex *exp) +{ + int ret = *exp->_p-'0'; + int positions = 10; + exp->_p++; + while(isdigit(*exp->_p)) { + ret = ret*10+(*exp->_p++-'0'); + if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant")); + positions *= 10; + }; + return ret; +} + +static int trex_element(TRex *exp) +{ + int ret = -1; + switch(*exp->_p) + { + case '(': { + int expr,newn; + exp->_p++; + + + if(*exp->_p =='?') { + exp->_p++; + trex_expect(exp,':'); + expr = trex_newnode(exp,OP_NOCAPEXPR); + } + else + expr = trex_newnode(exp,OP_EXPR); + newn = trex_list(exp); + exp->_nodes[expr].left = newn; + ret = expr; + trex_expect(exp,')'); + } + break; + case '[': + exp->_p++; + ret = trex_class(exp); + trex_expect(exp,']'); + break; + case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break; + case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break; + default: + ret = trex_charnode(exp,TRex_False); + break; + } + + { + TRexBool isgreedy = TRex_False; + unsigned short p0 = 0, p1 = 0; + switch(*exp->_p){ + case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break; + case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break; + case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break; + case '{': + exp->_p++; + if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected")); + p0 = (unsigned short)trex_parsenumber(exp); + /*******************************/ + switch(*exp->_p) { + case '}': + p1 = p0; exp->_p++; + break; + case ',': + exp->_p++; + p1 = 0xFFFF; + if(isdigit(*exp->_p)){ + p1 = (unsigned short)trex_parsenumber(exp); + } + trex_expect(exp,'}'); + break; + default: + trex_error(exp,_SC(", or } expected")); + } + /*******************************/ + isgreedy = TRex_True; + break; + + } + if(isgreedy) { + int nnode = trex_newnode(exp,OP_GREEDY); + exp->_nodes[nnode].left = ret; + exp->_nodes[nnode].right = ((p0)<<16)|p1; + ret = nnode; + } + } + if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) { + int nnode = trex_element(exp); + exp->_nodes[ret].next = nnode; + } + + return ret; +} + +static int trex_list(TRex *exp) +{ + int ret=-1,e; + if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) { + exp->_p++; + ret = trex_newnode(exp,OP_BOL); + } + e = trex_element(exp); + if(ret != -1) { + exp->_nodes[ret].next = e; + } + else ret = e; + + if(*exp->_p == TREX_SYMBOL_BRANCH) { + int temp,tright; + exp->_p++; + temp = trex_newnode(exp,OP_OR); + exp->_nodes[temp].left = ret; + tright = trex_list(exp); + exp->_nodes[temp].right = tright; + ret = temp; + } + return ret; +} + +static TRexBool trex_matchcclass(int cclass,TRexChar c) +{ + switch(cclass) { + case 'a': return isalpha(c)?TRex_True:TRex_False; + case 'A': return !isalpha(c)?TRex_True:TRex_False; + case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False; + case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False; + case 's': return ISSPACE(c)?TRex_True:TRex_False; + case 'S': return !ISSPACE(c)?TRex_True:TRex_False; + case 'd': return isdigit(c)?TRex_True:TRex_False; + case 'D': return !isdigit(c)?TRex_True:TRex_False; + case 'x': return isxdigit(c)?TRex_True:TRex_False; + case 'X': return !isxdigit(c)?TRex_True:TRex_False; + case 'c': return iscntrl(c)?TRex_True:TRex_False; + case 'C': return !iscntrl(c)?TRex_True:TRex_False; + case 'p': return ispunct(c)?TRex_True:TRex_False; + case 'P': return !ispunct(c)?TRex_True:TRex_False; + case 'l': return islower(c)?TRex_True:TRex_False; + case 'u': return isupper(c)?TRex_True:TRex_False; + } + return TRex_False; /*cannot happen*/ +} + +static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c) +{ + do { + switch(node->type) { + case OP_RANGE: + if (exp->_flags & TREX_ICASE) + { + if(c >= toupper(node->left) && c <= toupper(node->right)) return TRex_True; + if(c >= tolower(node->left) && c <= tolower(node->right)) return TRex_True; + } + else + { + if(c >= node->left && c <= node->right) return TRex_True; + } + break; + case OP_CCLASS: + if(trex_matchcclass(node->left,c)) return TRex_True; + break; + default: + if (exp->_flags & TREX_ICASE) + { + if (c == tolower(node->type) || c == toupper(node->type)) return TRex_True; + } + else + { + if(c == node->type)return TRex_True; + } + + } + } while((node->next != -1) && (node = &exp->_nodes[node->next])); + return TRex_False; +} + +static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next) +{ + + TRexNodeType type = node->type; + switch(type) { + case OP_GREEDY: { + //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL; + TRexNode *greedystop = NULL; + int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0; + const TRexChar *s=str, *good = str; + + if(node->next != -1) { + greedystop = &exp->_nodes[node->next]; + } + else { + greedystop = next; + } + + while((nmaches == 0xFFFF || nmaches < p1)) { + + const TRexChar *stop; + if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop))) + break; + nmaches++; + good=s; + if(greedystop) { + //checks that 0 matches satisfy the expression(if so skips) + //if not would always stop(for instance if is a '?') + if(greedystop->type != OP_GREEDY || + (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0)) + { + TRexNode *gnext = NULL; + if(greedystop->next != -1) { + gnext = &exp->_nodes[greedystop->next]; + }else if(next && next->next != -1){ + gnext = &exp->_nodes[next->next]; + } + stop = trex_matchnode(exp,greedystop,s,gnext); + if(stop) { + //if satisfied stop it + if(p0 == p1 && p0 == nmaches) break; + else if(nmaches >= p0 && p1 == 0xFFFF) break; + else if(nmaches >= p0 && nmaches <= p1) break; + } + } + } + + if(s >= exp->_eol) + break; + } + if(p0 == p1 && p0 == nmaches) return good; + else if(nmaches >= p0 && p1 == 0xFFFF) return good; + else if(nmaches >= p0 && nmaches <= p1) return good; + return NULL; + } + case OP_OR: { + const TRexChar *asd = str; + TRexNode *temp=&exp->_nodes[node->left]; + while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) { + if(temp->next != -1) + temp = &exp->_nodes[temp->next]; + else + return asd; + } + asd = str; + temp = &exp->_nodes[node->right]; + while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) { + if(temp->next != -1) + temp = &exp->_nodes[temp->next]; + else + return asd; + } + return NULL; + break; + } + case OP_EXPR: + case OP_NOCAPEXPR:{ + TRexNode *n = &exp->_nodes[node->left]; + const TRexChar *cur = str; + int capture = -1; + if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) { + capture = exp->_currsubexp; + exp->_matches[capture].begin = cur; + exp->_currsubexp++; + } + + do { + TRexNode *subnext = NULL; + if(n->next != -1) { + subnext = &exp->_nodes[n->next]; + }else { + subnext = next; + } + if(!(cur = trex_matchnode(exp,n,cur,subnext))) { + if(capture != -1){ + exp->_matches[capture].begin = 0; + exp->_matches[capture].len = 0; + } + return NULL; + } + } while((n->next != -1) && (n = &exp->_nodes[n->next])); + + if(capture != -1) + exp->_matches[capture].len = (int)(cur - exp->_matches[capture].begin); + return cur; + } + case OP_WB: + if((str == exp->_bol && !ISSPACE(*str)) + || ((str == exp->_eol && !ISSPACE(*(str-1)))) + || ((!ISSPACE(*str) && ISSPACE(*(str+1)))) + || ((ISSPACE(*str) && !ISSPACE(*(str+1)))) ) { + return (node->left == 'b')?str:NULL; + } + return (node->left == 'b')?NULL:str; + case OP_BOL: + if(str == exp->_bol) return str; + return NULL; + case OP_EOL: + if(str == exp->_eol) return str; + return NULL; + case OP_DOT: + str++; + return str; + case OP_NCLASS: + case OP_CLASS: + if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) { + str++; + return str; + } + return NULL; + case OP_CCLASS: + if(trex_matchcclass(node->left,*str)) { + str++; + return str; + } + return NULL; + default: /* char */ + if (exp->_flags & TREX_ICASE) + { + if(*str != tolower(node->type) && *str != toupper(node->type)) return NULL; + } + else + { + if (*str != node->type) return NULL; + } + str++; + return str; + } + return NULL; +} + +/* public api */ +TRex *trex_compile(const TRexChar *pattern,const TRexChar **error,int flags) +{ + TRex *exp = (TRex *)malloc(sizeof(TRex)); + exp->_eol = exp->_bol = NULL; + exp->_p = pattern; + exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar); + exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode)); + 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)); + exp->_flags = flags; + if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) { + int res = trex_list(exp); + exp->_nodes[exp->_first].left = res; + if(*exp->_p!='\0') + trex_error(exp,_SC("unexpected character")); +#ifdef _DEBUG + { + int nsize,i; + TRexNode *t; + nsize = exp->_nsize; + t = &exp->_nodes[0]; + scprintf(_SC("\n")); + for(i = 0;i < nsize; i++) { + if(exp->_nodes[i].type>MAX_CHAR) + scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]); + else + scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type); + scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next); + } + scprintf(_SC("\n")); + } +#endif + exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch)); + memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch)); + } + else{ + trex_free(exp); + return NULL; + } + return exp; +} + +void trex_free(TRex *exp) +{ + if(exp) { + if(exp->_nodes) free(exp->_nodes); + if(exp->_jmpbuf) free(exp->_jmpbuf); + if(exp->_matches) free(exp->_matches); + free(exp); + } +} + +TRexBool trex_match(TRex* exp,const TRexChar* text) +{ + const TRexChar* res = NULL; + exp->_bol = text; + exp->_eol = text + scstrlen(text); + exp->_currsubexp = 0; + res = trex_matchnode(exp,exp->_nodes,text,NULL); + if(res == NULL || res != exp->_eol) + return TRex_False; + return TRex_True; +} + +TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end) +{ + const TRexChar *cur = NULL; + int node = exp->_first; + if(text_begin >= text_end) return TRex_False; + exp->_bol = text_begin; + exp->_eol = text_end; + do { + cur = text_begin; + while(node != -1) { + exp->_currsubexp = 0; + cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL); + if(!cur) + break; + node = exp->_nodes[node].next; + } + text_begin++; + } while(cur == NULL && text_begin != text_end); + + if(cur == NULL) + return TRex_False; + + --text_begin; + + if(out_begin) *out_begin = text_begin; + if(out_end) *out_end = cur; + return TRex_True; +} + +TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end) +{ + return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end); +} + +int trex_getsubexpcount(TRex* exp) +{ + return exp->_nsubexpr; +} + +TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp) +{ + if( n<0 || n >= exp->_nsubexpr) return TRex_False; + *subexp = exp->_matches[n]; + return TRex_True; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include + +#include "argtable3.h" + + +static void arg_str_resetfn(struct arg_str *parent) +{ + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + + +static int arg_str_scanfn(struct arg_str *parent, const char *argval) +{ + int errorcode = 0; + + if (parent->count == parent->hdr.maxcount) + { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } + else if (!argval) + { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent arguiment value unaltered but still count the argument. */ + parent->count++; + } + else + { + parent->sval[parent->count++] = argval; + } + + ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static int arg_str_checkfn(struct arg_str *parent) +{ + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + + ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); + return errorcode; +} + + +static void arg_str_errorfn( + struct arg_str *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) +{ + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch(errorcode) + { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + } +} + + +struct arg_str * arg_str0( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) +{ + return arg_strn(shortopts, longopts, datatype, 0, 1, glossary); +} + + +struct arg_str * arg_str1( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) +{ + return arg_strn(shortopts, longopts, datatype, 1, 1, glossary); +} + + +struct arg_str * arg_strn( + const char *shortopts, + const char *longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) +{ + size_t nbytes; + struct arg_str *result; + + /* should not allow this stupid error */ + /* we should return an error code warning this logic error */ + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_str) /* storage for struct arg_str */ + + maxcount * sizeof(char *); /* storage for sval[maxcount] array */ + + result = (struct arg_str *)malloc(nbytes); + if (result) + { + int i; + + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : ""; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_str_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_str_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_str_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_str_errorfn; + + /* store the sval[maxcount] array immediately after the arg_str struct */ + result->sval = (const char * *)(result + 1); + result->count = 0; + + /* foolproof the string pointers by initialising them to reference empty strings */ + for (i = 0; i < maxcount; i++) + result->sval[i] = ""; + } + + ARG_TRACE(("arg_strn() returns %p\n", result)); + return result; +} +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#include +#include +#include +#include + +#include "argtable3.h" + +static +void arg_register_error(struct arg_end *end, + void *parent, + int error, + const char *argval) +{ + /* printf("arg_register_error(%p,%p,%d,%s)\n",end,parent,error,argval); */ + if (end->count < end->hdr.maxcount) + { + end->error[end->count] = error; + end->parent[end->count] = parent; + end->argval[end->count] = argval; + end->count++; + } + else + { + end->error[end->hdr.maxcount - 1] = ARG_ELIMIT; + end->parent[end->hdr.maxcount - 1] = end; + end->argval[end->hdr.maxcount - 1] = NULL; + } +} + + +/* + * Return index of first table entry with a matching short option + * or -1 if no match was found. + */ +static +int find_shortoption(struct arg_hdr * *table, char shortopt) +{ + int tabindex; + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + if (table[tabindex]->shortopts && + strchr(table[tabindex]->shortopts, shortopt)) + return tabindex; + } + return -1; +} + + +struct longoptions +{ + int getoptval; + int noptions; + struct option *options; +}; + +#if 0 +static +void dump_longoptions(struct longoptions * longoptions) +{ + int i; + printf("getoptval = %d\n", longoptions->getoptval); + printf("noptions = %d\n", longoptions->noptions); + for (i = 0; i < longoptions->noptions; i++) + { + printf("options[%d].name = \"%s\"\n", + i, + longoptions->options[i].name); + printf("options[%d].has_arg = %d\n", i, longoptions->options[i].has_arg); + printf("options[%d].flag = %p\n", i, longoptions->options[i].flag); + printf("options[%d].val = %d\n", i, longoptions->options[i].val); + } +} +#endif + +static +struct longoptions * alloc_longoptions(struct arg_hdr * *table) +{ + struct longoptions *result; + size_t nbytes; + int noptions = 1; + size_t longoptlen = 0; + int tabindex; + + /* + * Determine the total number of option structs required + * by counting the number of comma separated long options + * in all table entries and return the count in noptions. + * note: noptions starts at 1 not 0 because we getoptlong + * requires a NULL option entry to terminate the option array. + * While we are at it, count the number of chars required + * to store private copies of all the longoption strings + * and return that count in logoptlen. + */ + tabindex = 0; + do + { + const char *longopts = table[tabindex]->longopts; + longoptlen += (longopts ? strlen(longopts) : 0) + 1; + while (longopts) + { + noptions++; + longopts = strchr(longopts + 1, ','); + } + } while(!(table[tabindex++]->flag & ARG_TERMINATOR)); + /*printf("%d long options consuming %d chars in total\n",noptions,longoptlen);*/ + + + /* allocate storage for return data structure as: */ + /* (struct longoptions) + (struct options)[noptions] + char[longoptlen] */ + nbytes = sizeof(struct longoptions) + + sizeof(struct option) * noptions + + longoptlen; + result = (struct longoptions *)malloc(nbytes); + if (result) + { + int option_index = 0; + char *store; + + result->getoptval = 0; + result->noptions = noptions; + result->options = (struct option *)(result + 1); + store = (char *)(result->options + noptions); + + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + const char *longopts = table[tabindex]->longopts; + + while(longopts && *longopts) + { + char *storestart = store; + + /* copy progressive longopt strings into the store */ + while (*longopts != 0 && *longopts != ',') + *store++ = *longopts++; + *store++ = 0; + if (*longopts == ',') + longopts++; + /*fprintf(stderr,"storestart=\"%s\"\n",storestart);*/ + + result->options[option_index].name = storestart; + result->options[option_index].flag = &(result->getoptval); + result->options[option_index].val = tabindex; + if (table[tabindex]->flag & ARG_HASOPTVALUE) + result->options[option_index].has_arg = 2; + else if (table[tabindex]->flag & ARG_HASVALUE) + result->options[option_index].has_arg = 1; + else + result->options[option_index].has_arg = 0; + + option_index++; + } + } + /* terminate the options array with a zero-filled entry */ + result->options[option_index].name = 0; + result->options[option_index].has_arg = 0; + result->options[option_index].flag = 0; + result->options[option_index].val = 0; + } + + /*dump_longoptions(result);*/ + return result; +} + +static +char * alloc_shortoptions(struct arg_hdr * *table) +{ + char *result; + size_t len = 2; + int tabindex; + + /* determine the total number of option chars required */ + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + struct arg_hdr *hdr = table[tabindex]; + len += 3 * (hdr->shortopts ? strlen(hdr->shortopts) : 0); + } + + result = malloc(len); + if (result) + { + char *res = result; + + /* add a leading ':' so getopt return codes distinguish */ + /* unrecognised option and options missing argument values */ + *res++ = ':'; + + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + struct arg_hdr *hdr = table[tabindex]; + const char *shortopts = hdr->shortopts; + while(shortopts && *shortopts) + { + *res++ = *shortopts++; + if (hdr->flag & ARG_HASVALUE) + *res++ = ':'; + if (hdr->flag & ARG_HASOPTVALUE) + *res++ = ':'; + } + } + /* null terminate the string */ + *res = 0; + } + + /*printf("alloc_shortoptions() returns \"%s\"\n",(result?result:"NULL"));*/ + return result; +} + + +/* return index of the table terminator entry */ +static +int arg_endindex(struct arg_hdr * *table) +{ + int tabindex = 0; + while (!(table[tabindex]->flag & ARG_TERMINATOR)) + tabindex++; + return tabindex; +} + + +static +void arg_parse_tagged(int argc, + char * *argv, + struct arg_hdr * *table, + struct arg_end *endtable) +{ + struct longoptions *longoptions; + char *shortoptions; + int copt; + + /*printf("arg_parse_tagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/ + + /* allocate short and long option arrays for the given opttable[]. */ + /* if the allocs fail then put an error msg in the last table entry. */ + longoptions = alloc_longoptions(table); + shortoptions = alloc_shortoptions(table); + if (!longoptions || !shortoptions) + { + /* one or both memory allocs failed */ + arg_register_error(endtable, endtable, ARG_EMALLOC, NULL); + /* free anything that was allocated (this is null safe) */ + free(shortoptions); + free(longoptions); + return; + } + + /*dump_longoptions(longoptions);*/ + + /* reset getopts internal option-index to zero, and disable error reporting */ + optind = 0; + opterr = 0; + + /* fetch and process args using getopt_long */ + while( (copt = + getopt_long(argc, argv, shortoptions, longoptions->options, + NULL)) != -1) + { + /* + printf("optarg='%s'\n",optarg); + printf("optind=%d\n",optind); + printf("copt=%c\n",(char)copt); + printf("optopt=%c (%d)\n",optopt, (int)(optopt)); + */ + switch(copt) + { + case 0: + { + int tabindex = longoptions->getoptval; + void *parent = table[tabindex]->parent; + /*printf("long option detected from argtable[%d]\n", tabindex);*/ + if (optarg && optarg[0] == 0 && + (table[tabindex]->flag & ARG_HASVALUE)) + { + /* printf(": long option %s requires an argument\n",argv[optind-1]); */ + arg_register_error(endtable, endtable, ARG_EMISSARG, + argv[optind - 1]); + /* continue to scan the (empty) argument value to enforce argument count checking */ + } + if (table[tabindex]->scanfn) + { + int errorcode = table[tabindex]->scanfn(parent, optarg); + if (errorcode != 0) + arg_register_error(endtable, parent, errorcode, optarg); + } + } + break; + + case '?': + /* + * getopt_long() found an unrecognised short option. + * if it was a short option its value is in optopt + * if it was a long option then optopt=0 + */ + switch (optopt) + { + case 0: + /*printf("?0 unrecognised long option %s\n",argv[optind-1]);*/ + arg_register_error(endtable, endtable, ARG_ELONGOPT, + argv[optind - 1]); + break; + default: + /*printf("?* unrecognised short option '%c'\n",optopt);*/ + arg_register_error(endtable, endtable, optopt, NULL); + break; + } + break; + + case ':': + /* + * getopt_long() found an option with its argument missing. + */ + /*printf(": option %s requires an argument\n",argv[optind-1]); */ + arg_register_error(endtable, endtable, ARG_EMISSARG, + argv[optind - 1]); + break; + + default: + { + /* getopt_long() found a valid short option */ + int tabindex = find_shortoption(table, (char)copt); + /*printf("short option detected from argtable[%d]\n", tabindex);*/ + if (tabindex == -1) + { + /* should never get here - but handle it just in case */ + /*printf("unrecognised short option %d\n",copt);*/ + arg_register_error(endtable, endtable, copt, NULL); + } + else + { + if (table[tabindex]->scanfn) + { + void *parent = table[tabindex]->parent; + int errorcode = table[tabindex]->scanfn(parent, optarg); + if (errorcode != 0) + arg_register_error(endtable, parent, errorcode, optarg); + } + } + break; + } + } + } + + free(shortoptions); + free(longoptions); +} + + +static +void arg_parse_untagged(int argc, + char * *argv, + struct arg_hdr * *table, + struct arg_end *endtable) +{ + int tabindex = 0; + int errorlast = 0; + const char *optarglast = NULL; + void *parentlast = NULL; + + /*printf("arg_parse_untagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/ + while (!(table[tabindex]->flag & ARG_TERMINATOR)) + { + void *parent; + int errorcode; + + /* if we have exhausted our argv[optind] entries then we have finished */ + if (optind >= argc) + { + /*printf("arg_parse_untagged(): argv[] exhausted\n");*/ + return; + } + + /* skip table entries with non-null long or short options (they are not untagged entries) */ + if (table[tabindex]->longopts || table[tabindex]->shortopts) + { + /*printf("arg_parse_untagged(): skipping argtable[%d] (tagged argument)\n",tabindex);*/ + tabindex++; + continue; + } + + /* skip table entries with NULL scanfn */ + if (!(table[tabindex]->scanfn)) + { + /*printf("arg_parse_untagged(): skipping argtable[%d] (NULL scanfn)\n",tabindex);*/ + tabindex++; + continue; + } + + /* attempt to scan the current argv[optind] with the current */ + /* table[tabindex] entry. If it succeeds then keep it, otherwise */ + /* try again with the next table[] entry. */ + parent = table[tabindex]->parent; + errorcode = table[tabindex]->scanfn(parent, argv[optind]); + if (errorcode == 0) + { + /* success, move onto next argv[optind] but stay with same table[tabindex] */ + /*printf("arg_parse_untagged(): argtable[%d] successfully matched\n",tabindex);*/ + optind++; + + /* clear the last tentative error */ + errorlast = 0; + } + else + { + /* failure, try same argv[optind] with next table[tabindex] entry */ + /*printf("arg_parse_untagged(): argtable[%d] failed match\n",tabindex);*/ + tabindex++; + + /* remember this as a tentative error we may wish to reinstate later */ + errorlast = errorcode; + optarglast = argv[optind]; + parentlast = parent; + } + + } + + /* if a tenative error still remains at this point then register it as a proper error */ + if (errorlast) + { + arg_register_error(endtable, parentlast, errorlast, optarglast); + optind++; + } + + /* only get here when not all argv[] entries were consumed */ + /* register an error for each unused argv[] entry */ + while (optind < argc) + { + /*printf("arg_parse_untagged(): argv[%d]=\"%s\" not consumed\n",optind,argv[optind]);*/ + arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind++]); + } + + return; +} + + +static +void arg_parse_check(struct arg_hdr * *table, struct arg_end *endtable) +{ + int tabindex = 0; + /* printf("arg_parse_check()\n"); */ + do + { + if (table[tabindex]->checkfn) + { + void *parent = table[tabindex]->parent; + int errorcode = table[tabindex]->checkfn(parent); + if (errorcode != 0) + arg_register_error(endtable, parent, errorcode, NULL); + } + } while(!(table[tabindex++]->flag & ARG_TERMINATOR)); +} + + +static +void arg_reset(void * *argtable) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex = 0; + /*printf("arg_reset(%p)\n",argtable);*/ + do + { + if (table[tabindex]->resetfn) + table[tabindex]->resetfn(table[tabindex]->parent); + } while(!(table[tabindex++]->flag & ARG_TERMINATOR)); +} + + +int arg_parse(int argc, char * *argv, void * *argtable) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + struct arg_end *endtable; + int endindex; + char * *argvcopy = NULL; + + /*printf("arg_parse(%d,%p,%p)\n",argc,argv,argtable);*/ + + /* reset any argtable data from previous invocations */ + arg_reset(argtable); + + /* locate the first end-of-table marker within the array */ + endindex = arg_endindex(table); + endtable = (struct arg_end *)table[endindex]; + + /* Special case of argc==0. This can occur on Texas Instruments DSP. */ + /* Failure to trap this case results in an unwanted NULL result from */ + /* the malloc for argvcopy (next code block). */ + if (argc == 0) + { + /* We must still perform post-parse checks despite the absence of command line arguments */ + arg_parse_check(table, endtable); + + /* Now we are finished */ + return endtable->count; + } + + argvcopy = (char **)malloc(sizeof(char *) * (argc + 1)); + if (argvcopy) + { + int i; + + /* + Fill in the local copy of argv[]. We need a local copy + because getopt rearranges argv[] which adversely affects + susbsequent parsing attempts. + */ + for (i = 0; i < argc; i++) + argvcopy[i] = argv[i]; + + argvcopy[argc] = NULL; + + /* parse the command line (local copy) for tagged options */ + arg_parse_tagged(argc, argvcopy, table, endtable); + + /* parse the command line (local copy) for untagged options */ + arg_parse_untagged(argc, argvcopy, table, endtable); + + /* if no errors so far then perform post-parse checks otherwise dont bother */ + if (endtable->count == 0) + arg_parse_check(table, endtable); + + /* release the local copt of argv[] */ + free(argvcopy); + } + else + { + /* memory alloc failed */ + arg_register_error(endtable, endtable, ARG_EMALLOC, NULL); + } + + return endtable->count; +} + + +/* + * Concatenate contents of src[] string onto *pdest[] string. + * The *pdest pointer is altered to point to the end of the + * target string and *pndest is decremented by the same number + * of chars. + * Does not append more than *pndest chars into *pdest[] + * so as to prevent buffer overruns. + * Its something like strncat() but more efficient for repeated + * calls on the same destination string. + * Example of use: + * char dest[30] = "good" + * size_t ndest = sizeof(dest); + * char *pdest = dest; + * arg_char(&pdest,"bye ",&ndest); + * arg_char(&pdest,"cruel ",&ndest); + * arg_char(&pdest,"world!",&ndest); + * Results in: + * dest[] == "goodbye cruel world!" + * ndest == 10 + */ +static +void arg_cat(char * *pdest, const char *src, size_t *pndest) +{ + char *dest = *pdest; + char *end = dest + *pndest; + + /*locate null terminator of dest string */ + while(dest < end && *dest != 0) + dest++; + + /* concat src string to dest string */ + while(dest < end && *src != 0) + *dest++ = *src++; + + /* null terminate dest string */ + *dest = 0; + + /* update *pdest and *pndest */ + *pndest = end - dest; + *pdest = dest; +} + + +static +void arg_cat_option(char *dest, + size_t ndest, + const char *shortopts, + const char *longopts, + const char *datatype, + int optvalue) +{ + if (shortopts) + { + char option[3]; + + /* note: option array[] is initialiazed dynamically here to satisfy */ + /* a deficiency in the watcom compiler wrt static array initializers. */ + option[0] = '-'; + option[1] = shortopts[0]; + option[2] = 0; + + arg_cat(&dest, option, &ndest); + if (datatype) + { + arg_cat(&dest, " ", &ndest); + if (optvalue) + { + arg_cat(&dest, "[", &ndest); + arg_cat(&dest, datatype, &ndest); + arg_cat(&dest, "]", &ndest); + } + else + arg_cat(&dest, datatype, &ndest); + } + } + else if (longopts) + { + size_t ncspn; + + /* add "--" tag prefix */ + arg_cat(&dest, "--", &ndest); + + /* add comma separated option tag */ + ncspn = strcspn(longopts, ","); +#ifdef __STDC_WANT_SECURE_LIB__ + strncat_s(dest, ndest, longopts, (ncspn < ndest) ? ncspn : ndest); +#else + strncat(dest, longopts, (ncspn < ndest) ? ncspn : ndest); +#endif + + if (datatype) + { + arg_cat(&dest, "=", &ndest); + if (optvalue) + { + arg_cat(&dest, "[", &ndest); + arg_cat(&dest, datatype, &ndest); + arg_cat(&dest, "]", &ndest); + } + else + arg_cat(&dest, datatype, &ndest); + } + } + else if (datatype) + { + if (optvalue) + { + arg_cat(&dest, "[", &ndest); + arg_cat(&dest, datatype, &ndest); + arg_cat(&dest, "]", &ndest); + } + else + arg_cat(&dest, datatype, &ndest); + } +} + +static +void arg_cat_optionv(char *dest, + size_t ndest, + const char *shortopts, + const char *longopts, + const char *datatype, + int optvalue, + const char *separator) +{ + separator = separator ? separator : ""; + + if (shortopts) + { + const char *c = shortopts; + while(*c) + { + /* "-a|-b|-c" */ + char shortopt[3]; + + /* note: shortopt array[] is initialiazed dynamically here to satisfy */ + /* a deficiency in the watcom compiler wrt static array initializers. */ + shortopt[0] = '-'; + shortopt[1] = *c; + shortopt[2] = 0; + + arg_cat(&dest, shortopt, &ndest); + if (*++c) + arg_cat(&dest, separator, &ndest); + } + } + + /* put separator between long opts and short opts */ + if (shortopts && longopts) + arg_cat(&dest, separator, &ndest); + + if (longopts) + { + const char *c = longopts; + while(*c) + { + size_t ncspn; + + /* add "--" tag prefix */ + arg_cat(&dest, "--", &ndest); + + /* add comma separated option tag */ + ncspn = strcspn(c, ","); +#ifdef __STDC_WANT_SECURE_LIB__ + strncat_s(dest, ndest, c, (ncspn < ndest) ? ncspn : ndest); +#else + strncat(dest, c, (ncspn < ndest) ? ncspn : ndest); +#endif + c += ncspn; + + /* add given separator in place of comma */ + if (*c == ',') + { + arg_cat(&dest, separator, &ndest); + c++; + } + } + } + + if (datatype) + { + if (longopts) + arg_cat(&dest, "=", &ndest); + else if (shortopts) + arg_cat(&dest, " ", &ndest); + + if (optvalue) + { + arg_cat(&dest, "[", &ndest); + arg_cat(&dest, datatype, &ndest); + arg_cat(&dest, "]", &ndest); + } + else + arg_cat(&dest, datatype, &ndest); + } +} + + +/* this function should be deprecated because it doesnt consider optional argument values (ARG_HASOPTVALUE) */ +void arg_print_option(FILE *fp, + const char *shortopts, + const char *longopts, + const char *datatype, + const char *suffix) +{ + char syntax[200] = ""; + suffix = suffix ? suffix : ""; + + /* there is no way of passing the proper optvalue for optional argument values here, so we must ignore it */ + arg_cat_optionv(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + 0, + "|"); + + fputs(syntax, fp); + fputs(suffix, fp); +} + + +/* + * Print a GNU style [OPTION] string in which all short options that + * do not take argument values are presented in abbreviated form, as + * in: -xvfsd, or -xvf[sd], or [-xvsfd] + */ +static +void arg_print_gnuswitch(FILE *fp, struct arg_hdr * *table) +{ + int tabindex; + char *format1 = " -%c"; + char *format2 = " [-%c"; + char *suffix = ""; + + /* print all mandatory switches that are without argument values */ + for(tabindex = 0; + table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); + tabindex++) + { + /* skip optional options */ + if (table[tabindex]->mincount < 1) + continue; + + /* skip non-short options */ + if (table[tabindex]->shortopts == NULL) + continue; + + /* skip options that take argument values */ + if (table[tabindex]->flag & ARG_HASVALUE) + continue; + + /* print the short option (only the first short option char, ignore multiple choices)*/ + fprintf(fp, format1, table[tabindex]->shortopts[0]); + format1 = "%c"; + format2 = "[%c"; + } + + /* print all optional switches that are without argument values */ + for(tabindex = 0; + table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); + tabindex++) + { + /* skip mandatory args */ + if (table[tabindex]->mincount > 0) + continue; + + /* skip args without short options */ + if (table[tabindex]->shortopts == NULL) + continue; + + /* skip args with values */ + if (table[tabindex]->flag & ARG_HASVALUE) + continue; + + /* print first short option */ + fprintf(fp, format2, table[tabindex]->shortopts[0]); + format2 = "%c"; + suffix = "]"; + } + + fprintf(fp, "%s", suffix); +} + + +void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int i, tabindex; + + /* print GNU style [OPTION] string */ + arg_print_gnuswitch(fp, table); + + /* print remaining options in abbreviated style */ + for(tabindex = 0; + table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); + tabindex++) + { + char syntax[200] = ""; + const char *shortopts, *longopts, *datatype; + + /* skip short options without arg values (they were printed by arg_print_gnu_switch) */ + if (table[tabindex]->shortopts && + !(table[tabindex]->flag & ARG_HASVALUE)) + continue; + + shortopts = table[tabindex]->shortopts; + longopts = table[tabindex]->longopts; + datatype = table[tabindex]->datatype; + arg_cat_option(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + table[tabindex]->flag & ARG_HASOPTVALUE); + + if (strlen(syntax) > 0) + { + /* print mandatory instances of this option */ + for (i = 0; i < table[tabindex]->mincount; i++) + fprintf(fp, " %s", syntax); + + /* print optional instances enclosed in "[..]" */ + switch ( table[tabindex]->maxcount - table[tabindex]->mincount ) + { + case 0: + break; + case 1: + fprintf(fp, " [%s]", syntax); + break; + case 2: + fprintf(fp, " [%s] [%s]", syntax, syntax); + break; + default: + fprintf(fp, " [%s]...", syntax); + break; + } + } + } + + if (suffix) + fprintf(fp, "%s", suffix); +} + + +void arg_print_syntaxv(FILE *fp, void * *argtable, const char *suffix) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int i, tabindex; + + /* print remaining options in abbreviated style */ + for(tabindex = 0; + table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); + tabindex++) + { + char syntax[200] = ""; + const char *shortopts, *longopts, *datatype; + + shortopts = table[tabindex]->shortopts; + longopts = table[tabindex]->longopts; + datatype = table[tabindex]->datatype; + arg_cat_optionv(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + table[tabindex]->flag & ARG_HASOPTVALUE, + "|"); + + /* print mandatory options */ + for (i = 0; i < table[tabindex]->mincount; i++) + fprintf(fp, " %s", syntax); + + /* print optional args enclosed in "[..]" */ + switch ( table[tabindex]->maxcount - table[tabindex]->mincount ) + { + case 0: + break; + case 1: + fprintf(fp, " [%s]", syntax); + break; + case 2: + fprintf(fp, " [%s] [%s]", syntax, syntax); + break; + default: + fprintf(fp, " [%s]...", syntax); + break; + } + } + + if (suffix) + fprintf(fp, "%s", suffix); +} + + +void arg_print_glossary(FILE *fp, void * *argtable, const char *format) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex; + + format = format ? format : " %-20s %s\n"; + for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + if (table[tabindex]->glossary) + { + char syntax[200] = ""; + const char *shortopts = table[tabindex]->shortopts; + const char *longopts = table[tabindex]->longopts; + const char *datatype = table[tabindex]->datatype; + const char *glossary = table[tabindex]->glossary; + arg_cat_optionv(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + table[tabindex]->flag & ARG_HASOPTVALUE, + ", "); + fprintf(fp, format, syntax, glossary); + } + } +} + + +/** + * Print a piece of text formatted, which means in a column with a + * left and a right margin. The lines are wrapped at whitspaces next + * to right margin. The function does not indent the first line, but + * only the following ones. + * + * Example: + * arg_print_formatted( fp, 0, 5, "Some text that doesn't fit." ) + * will result in the following output: + * + * Some + * text + * that + * doesn' + * t fit. + * + * Too long lines will be wrapped in the middle of a word. + * + * arg_print_formatted( fp, 2, 7, "Some text that doesn't fit." ) + * will result in the following output: + * + * Some + * text + * that + * doesn' + * t fit. + * + * As you see, the first line is not indented. This enables output of + * lines, which start in a line where output already happened. + * + * Author: Uli Fouquet + */ +static +void arg_print_formatted( FILE *fp, + const unsigned lmargin, + const unsigned rmargin, + const char *text ) +{ + const unsigned textlen = (unsigned)strlen( text ); + unsigned line_start = 0; + unsigned line_end = textlen + 1; + const unsigned colwidth = (rmargin - lmargin) + 1; + + /* Someone doesn't like us... */ + if ( line_end < line_start ) + { fprintf( fp, "%s\n", text ); } + + while (line_end - 1 > line_start ) + { + /* Eat leading whitespaces. This is essential because while + wrapping lines, there will often be a whitespace at beginning + of line */ + while ( ISSPACE(*(text + line_start)) ) + { line_start++; } + + if ((line_end - line_start) > colwidth ) + { line_end = line_start + colwidth; } + + /* Find last whitespace, that fits into line */ + while ( ( line_end > line_start ) + && ( line_end - line_start > colwidth ) + && !ISSPACE(*(text + line_end))) + { line_end--; } + + /* Do not print trailing whitespace. If this text + has got only one line, line_end now points to the + last char due to initialization. */ + line_end--; + + /* Output line of text */ + while ( line_start < line_end ) + { + fputc(*(text + line_start), fp ); + line_start++; + } + fputc( '\n', fp ); + + /* Initialize another line */ + if ( line_end + 1 < textlen ) + { + unsigned i; + + for (i = 0; i < lmargin; i++ ) + { fputc( ' ', fp ); } + + line_end = textlen; + } + + /* If we have to print another line, get also the last char. */ + line_end++; + + } /* lines of text */ +} + +/** + * Prints the glossary in strict GNU format. + * Differences to arg_print_glossary() are: + * - wraps lines after 80 chars + * - indents lines without shortops + * - does not accept formatstrings + * + * Contributed by Uli Fouquet + */ +void arg_print_glossary_gnu(FILE *fp, void * *argtable ) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex; + + for(tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) + { + if (table[tabindex]->glossary) + { + char syntax[200] = ""; + const char *shortopts = table[tabindex]->shortopts; + const char *longopts = table[tabindex]->longopts; + const char *datatype = table[tabindex]->datatype; + const char *glossary = table[tabindex]->glossary; + + if ( !shortopts && longopts ) + { + /* Indent trailing line by 4 spaces... */ + memset( syntax, ' ', 4 ); + *(syntax + 4) = '\0'; + } + + arg_cat_optionv(syntax, + sizeof(syntax), + shortopts, + longopts, + datatype, + table[tabindex]->flag & ARG_HASOPTVALUE, + ", "); + + /* If syntax fits not into column, print glossary in new line... */ + if ( strlen(syntax) > 25 ) + { + fprintf( fp, " %-25s %s\n", syntax, "" ); + *syntax = '\0'; + } + + fprintf( fp, " %-25s ", syntax ); + arg_print_formatted( fp, 28, 79, glossary ); + } + } /* for each table entry */ + + fputc( '\n', fp ); +} + + +/** + * Checks the argtable[] array for NULL entries and returns 1 + * if any are found, zero otherwise. + */ +int arg_nullcheck(void * *argtable) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex; + /*printf("arg_nullcheck(%p)\n",argtable);*/ + + if (!table) + return 1; + + tabindex = 0; + do + { + /*printf("argtable[%d]=%p\n",tabindex,argtable[tabindex]);*/ + if (!table[tabindex]) + return 1; + } while(!(table[tabindex++]->flag & ARG_TERMINATOR)); + + return 0; +} + + +/* + * arg_free() is deprecated in favour of arg_freetable() due to a flaw in its design. + * The flaw results in memory leak in the (very rare) case that an intermediate + * entry in the argtable array failed its memory allocation while others following + * that entry were still allocated ok. Those subsequent allocations will not be + * deallocated by arg_free(). + * Despite the unlikeliness of the problem occurring, and the even unlikelier event + * that it has any deliterious effect, it is fixed regardless by replacing arg_free() + * with the newer arg_freetable() function. + * We still keep arg_free() for backwards compatibility. + */ +void arg_free(void * *argtable) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + int tabindex = 0; + int flag; + /*printf("arg_free(%p)\n",argtable);*/ + do + { + /* + if we encounter a NULL entry then somewhat incorrectly we presume + we have come to the end of the array. It isnt strictly true because + an intermediate entry could be NULL with other non-NULL entries to follow. + The subsequent argtable entries would then not be freed as they should. + */ + if (table[tabindex] == NULL) + break; + + flag = table[tabindex]->flag; + free(table[tabindex]); + table[tabindex++] = NULL; + + } while(!(flag & ARG_TERMINATOR)); +} + +/* frees each non-NULL element of argtable[], where n is the size of the number of entries in the array */ +void arg_freetable(void * *argtable, size_t n) +{ + struct arg_hdr * *table = (struct arg_hdr * *)argtable; + size_t tabindex = 0; + /*printf("arg_freetable(%p)\n",argtable);*/ + for (tabindex = 0; tabindex < n; tabindex++) + { + if (table[tabindex] == NULL) + continue; + + free(table[tabindex]); + table[tabindex] = NULL; + }; +} + diff --git a/client/cliparser/argtable3.h b/client/cliparser/argtable3.h new file mode 100644 index 00000000..1107de25 --- /dev/null +++ b/client/cliparser/argtable3.h @@ -0,0 +1,305 @@ +/******************************************************************************* + * This file is part of the argtable3 library. + * + * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of STEWART HEITMANN nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ******************************************************************************/ + +#ifndef ARGTABLE3 +#define ARGTABLE3 + +#include /* FILE */ +#include /* struct tm */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARG_REX_ICASE 1 + +/* bit masks for arg_hdr.flag */ +enum +{ + ARG_TERMINATOR=0x1, + ARG_HASVALUE=0x2, + ARG_HASOPTVALUE=0x4 +}; + +typedef void (arg_resetfn)(void *parent); +typedef int (arg_scanfn)(void *parent, const char *argval); +typedef int (arg_checkfn)(void *parent); +typedef void (arg_errorfn)(void *parent, FILE *fp, int error, const char *argval, const char *progname); + + +/* +* The arg_hdr struct defines properties that are common to all arg_xxx structs. +* The argtable library requires each arg_xxx struct to have an arg_hdr +* struct as its first data member. +* The argtable library functions then use this data to identify the +* properties of the command line option, such as its option tags, +* datatype string, and glossary strings, and so on. +* Moreover, the arg_hdr struct contains pointers to custom functions that +* are provided by each arg_xxx struct which perform the tasks of parsing +* that particular arg_xxx arguments, performing post-parse checks, and +* reporting errors. +* These functions are private to the individual arg_xxx source code +* and are the pointer to them are initiliased by that arg_xxx struct's +* constructor function. The user could alter them after construction +* if desired, but the original intention is for them to be set by the +* constructor and left unaltered. +*/ +struct arg_hdr +{ + char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */ + const char *shortopts; /* String defining the short options */ + const char *longopts; /* String defiing the long options */ + const char *datatype; /* Description of the argument data type */ + const char *glossary; /* Description of the option as shown by arg_print_glossary function */ + int mincount; /* Minimum number of occurences of this option accepted */ + int maxcount; /* Maximum number of occurences if this option accepted */ + void *parent; /* Pointer to parent arg_xxx struct */ + arg_resetfn *resetfn; /* Pointer to parent arg_xxx reset function */ + arg_scanfn *scanfn; /* Pointer to parent arg_xxx scan function */ + arg_checkfn *checkfn; /* Pointer to parent arg_xxx check function */ + arg_errorfn *errorfn; /* Pointer to parent arg_xxx error function */ + void *priv; /* Pointer to private header data for use by arg_xxx functions */ +}; + +struct arg_rem +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ +}; + +struct arg_lit +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ +}; + +struct arg_int +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ + int *ival; /* Array of parsed argument values */ +}; + +struct arg_dbl +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ + double *dval; /* Array of parsed argument values */ +}; + +struct arg_str +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ + const char **sval; /* Array of parsed argument values */ +}; + +struct arg_rex +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ + const char **sval; /* Array of parsed argument values */ +}; + +struct arg_file +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args*/ + const char **filename; /* Array of parsed filenames (eg: /home/foo.bar) */ + const char **basename; /* Array of parsed basenames (eg: foo.bar) */ + const char **extension; /* Array of parsed extensions (eg: .bar) */ +}; + +struct arg_date +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + const char *format; /* strptime format string used to parse the date */ + int count; /* Number of matching command line args */ + struct tm *tmval; /* Array of parsed time values */ +}; + +enum {ARG_ELIMIT=1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG}; +struct arg_end +{ + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of errors encountered */ + int *error; /* Array of error codes */ + void **parent; /* Array of pointers to offending arg_xxx struct */ + const char **argval; /* Array of pointers to offending argv[] string */ +}; + + +/**** arg_xxx constructor functions *********************************/ + +struct arg_rem* arg_rem(const char* datatype, const char* glossary); + +struct arg_lit* arg_lit0(const char* shortopts, + const char* longopts, + const char* glossary); +struct arg_lit* arg_lit1(const char* shortopts, + const char* longopts, + const char *glossary); +struct arg_lit* arg_litn(const char* shortopts, + const char* longopts, + int mincount, + int maxcount, + const char *glossary); + +struct arg_key* arg_key0(const char* keyword, + int flags, + const char* glossary); +struct arg_key* arg_key1(const char* keyword, + int flags, + const char* glossary); +struct arg_key* arg_keyn(const char* keyword, + int flags, + int mincount, + int maxcount, + const char* glossary); + +struct arg_int* arg_int0(const char* shortopts, + const char* longopts, + const char* datatype, + const char* glossary); +struct arg_int* arg_int1(const char* shortopts, + const char* longopts, + const char* datatype, + const char *glossary); +struct arg_int* arg_intn(const char* shortopts, + const char* longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_dbl* arg_dbl0(const char* shortopts, + const char* longopts, + const char* datatype, + const char* glossary); +struct arg_dbl* arg_dbl1(const char* shortopts, + const char* longopts, + const char* datatype, + const char *glossary); +struct arg_dbl* arg_dbln(const char* shortopts, + const char* longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_str* arg_str0(const char* shortopts, + const char* longopts, + const char* datatype, + const char* glossary); +struct arg_str* arg_str1(const char* shortopts, + const char* longopts, + const char* datatype, + const char *glossary); +struct arg_str* arg_strn(const char* shortopts, + const char* longopts, + const char* datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_rex* arg_rex0(const char* shortopts, + const char* longopts, + const char* pattern, + const char* datatype, + int flags, + const char* glossary); +struct arg_rex* arg_rex1(const char* shortopts, + const char* longopts, + const char* pattern, + const char* datatype, + int flags, + const char *glossary); +struct arg_rex* arg_rexn(const char* shortopts, + const char* longopts, + const char* pattern, + const char* datatype, + int mincount, + int maxcount, + int flags, + const char *glossary); + +struct arg_file* arg_file0(const char* shortopts, + const char* longopts, + const char* datatype, + const char* glossary); +struct arg_file* arg_file1(const char* shortopts, + const char* longopts, + const char* datatype, + const char *glossary); +struct arg_file* arg_filen(const char* shortopts, + const char* longopts, + const char* datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_date* arg_date0(const char* shortopts, + const char* longopts, + const char* format, + const char* datatype, + const char* glossary); +struct arg_date* arg_date1(const char* shortopts, + const char* longopts, + const char* format, + const char* datatype, + const char *glossary); +struct arg_date* arg_daten(const char* shortopts, + const char* longopts, + const char* format, + const char* datatype, + int mincount, + int maxcount, + const char *glossary); + +struct arg_end* arg_end(int maxerrors); + + +/**** other functions *******************************************/ +int arg_nullcheck(void **argtable); +int arg_parse(int argc, char **argv, void **argtable); +void arg_print_option(FILE *fp, const char *shortopts, const char *longopts, const char *datatype, const char *suffix); +void arg_print_syntax(FILE *fp, void **argtable, const char *suffix); +void arg_print_syntaxv(FILE *fp, void **argtable, const char *suffix); +void arg_print_glossary(FILE *fp, void **argtable, const char *format); +void arg_print_glossary_gnu(FILE *fp, void **argtable); +void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname); +void arg_freetable(void **argtable, size_t n); + +/**** deprecated functions, for back-compatibility only ********/ +void arg_free(void **argtable); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/client/cliparser/cliparser.c b/client/cliparser/cliparser.c new file mode 100644 index 00000000..95422039 --- /dev/null +++ b/client/cliparser/cliparser.c @@ -0,0 +1,205 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Command line parser core commands +//----------------------------------------------------------------------------- + +#include "cliparser.h" +#include +#include + +void **argtable = NULL; +size_t argtableLen = 0; +char *programName = NULL; +char *programHint = NULL; +char *programHelp = NULL; +char buf[500] = {0}; + +int CLIParserInit(char *vprogramName, char *vprogramHint, char *vprogramHelp) { + argtable = NULL; + argtableLen = 0; + programName = vprogramName; + programHint = vprogramHint; + programHelp = vprogramHelp; + memset(buf, 0x00, 500); + + return 0; +} + +int CLIParserParseArg(int argc, char **argv, void* vargtable[], size_t vargtableLen, bool allowEmptyExec) { + int nerrors; + + argtable = vargtable; + argtableLen = vargtableLen; + + /* verify the argtable[] entries were allocated sucessfully */ + if (arg_nullcheck(argtable) != 0) { + /* NULL entries were detected, some allocations must have failed */ + printf("ERROR: Insufficient memory\n"); + return 2; + } + /* Parse the command line as defined by argtable[] */ + nerrors = arg_parse(argc, argv, argtable); + + /* special case: '--help' takes precedence over error reporting */ + if ((argc < 2 && !allowEmptyExec) ||((struct arg_lit *)argtable[0])->count > 0) { // help must be the first record + printf("Usage: %s", programName); + arg_print_syntaxv(stdout, argtable, "\n"); + if (programHint) + printf("%s\n\n", programHint); + arg_print_glossary(stdout, argtable, " %-20s %s\n"); + printf("\n"); + if (programHelp) + printf("%s \n", programHelp); + + return 1; + } + + /* If the parser returned any errors then display them and exit */ + if (nerrors > 0) { + /* Display the error details contained in the arg_end struct.*/ + arg_print_errors(stdout, ((struct arg_end *)argtable[vargtableLen - 1]), programName); + printf("Try '%s --help' for more information.\n", programName); + + return 3; + } + + return 0; +} + +enum ParserState { + PS_FIRST, + PS_ARGUMENT, + PS_OPTION, +}; + +#define isSpace(c)(c == ' ' || c == '\t') + +int CLIParserParseString(const char* str, void* vargtable[], size_t vargtableLen, bool allowEmptyExec) { + return CLIParserParseStringEx(str, vargtable, vargtableLen, allowEmptyExec, false); +} + +int CLIParserParseStringEx(const char* str, void* vargtable[], size_t vargtableLen, bool allowEmptyExec, bool clueData) { + int argc = 0; + char *argv[200] = {NULL}; + + int len = strlen(str); + char *bufptr = buf; + char *spaceptr = NULL; + enum ParserState state = PS_FIRST; + + argv[argc++] = bufptr; + // param0 = program name + memcpy(buf, programName, strlen(programName) + 1); // with 0x00 + bufptr += strlen(programName) + 1; + if (len) + argv[argc++] = bufptr; + + // parse params + for (int i = 0; i < len; i++) { + switch(state){ + 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; + + if (spaceptr) { + bufptr = spaceptr; + *bufptr = 0x00; + bufptr++; + argv[argc++] = bufptr; + } + } + spaceptr = NULL; + case PS_ARGUMENT: + if (state == PS_FIRST) + state = PS_ARGUMENT; + if (isSpace(str[i])) { + spaceptr = bufptr; + state = PS_FIRST; + } + *bufptr = str[i]; + bufptr++; + break; + case PS_OPTION: + if (isSpace(str[i])){ + state = PS_FIRST; + + *bufptr = 0x00; + bufptr++; + argv[argc++] = bufptr; + break; + } + + *bufptr = str[i]; + bufptr++; + break; + } + } + + return CLIParserParseArg(argc, argv, vargtable, vargtableLen, allowEmptyExec); +} + +void CLIParserFree() { + arg_freetable(argtable, argtableLen); + argtable = NULL; + + return; +} + +// convertors +int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { + *datalen = 0; + + int ibuf = 0; + uint8_t buf[256] = {0}; + int res = CLIParamStrToBuf(argstr, buf, maxdatalen * 2, &ibuf); // *2 because here HEX + if (res || !ibuf) + return res; + + switch(param_gethex_to_eol((char *)buf, 0, data, maxdatalen, datalen)) { + case 1: + printf("Parameter error: Invalid HEX value.\n"); + return 1; + case 2: + printf("Parameter error: parameter too large.\n"); + return 2; + case 3: + printf("Parameter error: Hex string must have even number of digits.\n"); + return 3; + } + + return 0; +} + +int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { + *datalen = 0; + if (!argstr->count) + return 0; + + uint8_t buf[256] = {0}; + int ibuf = 0; + + for (int i = 0; i < argstr->count; i++) { + int len = strlen(argstr->sval[i]); + memcpy(&buf[ibuf], argstr->sval[i], len); + ibuf += len; + } + buf[ibuf] = 0; + + if (!ibuf) + return 0; + + if (ibuf > maxdatalen) + return 2; + + memcpy(data, buf, ibuf); + *datalen = ibuf; + + return 0; +} + + diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h new file mode 100644 index 00000000..05910ea4 --- /dev/null +++ b/client/cliparser/cliparser.h @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Command line parser core commands +//----------------------------------------------------------------------------- + +#include "argtable3.h" +#include "util.h" +#include + +#define arg_param_begin arg_lit0("hH", "help", "print this help and exit") +#define arg_param_end arg_end(20) + +#define arg_getsize(a) (sizeof(a) / sizeof(a[0])) +#define arg_get_lit(n)(((struct arg_lit*)argtable[n])->count) +#define arg_get_int_count(n)(((struct arg_int*)argtable[n])->count) +#define arg_get_int(n)(((struct arg_int*)argtable[n])->ival[0]) +#define arg_get_int_def(n,def)(arg_get_int_count(n)?(arg_get_int(n)):(def)) +#define arg_get_str(n)((struct arg_str*)argtable[n]) +#define arg_get_str_len(n)(strlen(((struct arg_str*)argtable[n])->sval[0])) + +#define arg_strx1(shortopts, longopts, datatype, glossary) (arg_strn((shortopts), (longopts), (datatype), 1, 250, (glossary))) +#define arg_strx0(shortopts, longopts, datatype, glossary) (arg_strn((shortopts), (longopts), (datatype), 0, 250, (glossary))) + +#define CLIExecWithReturn(cmd, atbl, ifempty) if (CLIParserParseString(cmd, atbl, arg_getsize(atbl), ifempty)){CLIParserFree();return 0;} +#define CLIGetHexBLessWithReturn(paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree();return 1;} +#define CLIGetHexWithReturn(paramnum, data, datalen) if (CLIParamHexToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;} +#define CLIGetStrWithReturn(paramnum, data, datalen) if (CLIParamStrToBuf(arg_get_str(paramnum), data, sizeof(data), datalen)) {CLIParserFree();return 1;} + +extern int CLIParserInit(char *vprogramName, char *vprogramHint, char *vprogramHelp); +extern int CLIParserParseString(const char* str, void* argtable[], size_t vargtableLen, bool allowEmptyExec); +extern int CLIParserParseStringEx(const char* str, void* vargtable[], size_t vargtableLen, bool allowEmptyExec, bool clueData); +extern int CLIParserParseArg(int argc, char **argv, void* argtable[], size_t vargtableLen, bool allowEmptyExec); +extern void CLIParserFree(); + +extern int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); +extern int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); diff --git a/client/cmdcrc.c b/client/cmdcrc.c deleted file mode 100644 index 01f65f55..00000000 --- a/client/cmdcrc.c +++ /dev/null @@ -1,547 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2015 iceman -// -// This code is licensed to you under the terms of the GNU GPL, version 2 or, -// at your option, any later version. See the LICENSE.txt file for the text of -// the license. -//----------------------------------------------------------------------------- -// CRC Calculations from the software reveng commands -//----------------------------------------------------------------------------- - -#ifdef _WIN32 -# include -# include -# ifndef STDIN_FILENO -# define STDIN_FILENO 0 -# endif /* STDIN_FILENO */ -#endif /* _WIN32 */ - -#include -#include -#include -#include -#include "cmdmain.h" -#include "cmdcrc.h" -#include "reveng/reveng.h" -#include "ui.h" -#include "util.h" - -#define MAX_ARGS 20 - -int uerr(char *msg){ - PrintAndLog("%s",msg); - return 0; -} - -int split(char *str, char *arr[MAX_ARGS]){ - int beginIndex = 0; - int endIndex; - int maxWords = MAX_ARGS; - int wordCnt = 0; - - while(1){ - while(isspace(str[beginIndex])){ - ++beginIndex; - } - if(str[beginIndex] == '\0') { - break; - } - endIndex = beginIndex; - while (str[endIndex] && !isspace(str[endIndex])){ - ++endIndex; - } - int len = endIndex - beginIndex; - char *tmp = calloc(len + 1, sizeof(char)); - memcpy(tmp, &str[beginIndex], len); - arr[wordCnt++] = tmp; - //PrintAndLog("DEBUG cnt: %d, %s",wordCnt-1, arr[wordCnt-1]); - beginIndex = endIndex; - if (wordCnt == maxWords) - break; - } - return wordCnt; -} - -int CmdCrc(const char *Cmd) -{ - char name[] = {"reveng "}; - char Cmd2[50 + 7]; - memcpy(Cmd2, name, 7); - memcpy(Cmd2 + 7, Cmd, 50); - char *argv[MAX_ARGS]; - int argc = split(Cmd2, argv); - - if (argc == 3 && memcmp(argv[1],"-g",2)==0) { - CmdrevengSearch(argv[2]); - } else { - reveng_main(argc, argv); - } - //PrintAndLog("DEBUG argc: %d, %s %s Cmd: %s",argc, argv[0], Cmd2, Cmd); - for(int i = 0; i < argc; ++i){ - free(argv[i]); - } - - return 0; -} - -//returns array of model names and the count of models returning -// as well as a width array for the width of each model -int GetModels(char *Models[], int *count, uint8_t *width){ - /* default values */ - static model_t model = { - PZERO, /* no CRC polynomial, user must specify */ - PZERO, /* Init = 0 */ - P_BE, /* RefIn = false, RefOut = false, plus P_RTJUST setting in reveng.h */ - PZERO, /* XorOut = 0 */ - PZERO, /* check value unused */ - NULL /* no model name */ - }; - - int ibperhx = 8;//, obperhx = 8; - int rflags = 0, uflags = 0; /* search and UI flags */ - poly_t apoly, crc, qpoly = PZERO, *apolys = NULL, *pptr = NULL, *qptr = NULL; - model_t pset = model, *candmods, *mptr; - - /* stdin must be binary */ - #ifdef _WIN32 - _setmode(STDIN_FILENO, _O_BINARY); - #endif /* _WIN32 */ - - SETBMP(); - - int args = 0, psets, pass; - int Cnt = 0; - if (width[0] == 0) { //reveng -D - *count = mcount(); - if(!*count) - return uerr("no preset models available"); - - for(int mode = 0; mode < *count; ++mode) { - mbynum(&model, mode); - mcanon(&model); - size_t size = (model.name && *model.name) ? strlen(model.name) : 6; - char *tmp = calloc(size+1, sizeof(char)); - if (tmp==NULL) - return uerr("out of memory?"); - - memcpy(tmp, model.name, size); - Models[mode] = tmp; - width[mode] = plen(model.spoly); - } - mfree(&model); - } else { //reveng -s - - if(~model.flags & P_MULXN) - return uerr("cannot search for non-Williams compliant models"); - - 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)) - palloc(&model.spoly, (unsigned long)width[0]); - else - width[0] = (uint8_t)plen(model.spoly); - - /* special case if qpoly is zero, search to end of range */ - if(!ptst(qpoly)) - rflags &= ~R_HAVEQ; - - - /* not going to be sending additional args at this time (maybe future?) - - // allocate argument array - args = argc - optind; - if(!(apolys = malloc(args * sizeof(poly_t)))) - return uerr("cannot allocate memory for argument list"); - - for(pptr = apolys; optind < argc; ++optind) { - if(uflags & C_INFILE) - *pptr++ = rdpoly(argv[optind], model.flags, ibperhx); - else - *pptr++ = strtop(argv[optind], model.flags, ibperhx); - } - // exit value of pptr is used hereafter! - - */ - - /* if endianness not specified, try - * little-endian then big-endian. - * NB: crossed-endian algorithms will not be - * searched. - */ - /* scan against preset models */ - if(~uflags & C_FORCE) { - pass = 0; - Cnt = 0; - do { - psets = mcount(); - //PrintAndLog("psets: %d",psets); - 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)) - continue; - /* skip if the preset doesn't match specified parameters */ - if(rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly)) - continue; - if(rflags & R_HAVEI && psncmp(&model.init, &pset.init)) - continue; - 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) - prev(&apoly); - - for(qptr = apolys; qptr < pptr; ++qptr) { - crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0); - if(ptst(crc)) { - pfree(&crc); - break; - } else - pfree(&crc); - } - pfree(&apoly); - if(qptr == pptr) { - - /* the selected model solved all arguments */ - - mcanon(&pset); - - size_t size = (pset.name && *pset.name) ? strlen(pset.name) : 6; - //PrintAndLog("Size: %d, %s, count: %d",size,pset.name, Cnt); - char *tmp = calloc(size+1, sizeof(char)); - if (tmp==NULL){ - PrintAndLog("out of memory?"); - return 0; - } - width[Cnt] = width[0]; - memcpy(tmp, pset.name, size); - Models[Cnt++] = tmp; - *count = Cnt; - uflags |= C_RESULT; - } - } - mfree(&pset); - - /* toggle refIn/refOut and reflect arguments */ - 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); - } - //got everything now free the memory... - - if(uflags & C_RESULT) { - for(qptr = apolys; qptr < pptr; ++qptr) - pfree(qptr); - } - if(!(model.flags & P_REFIN) != !(model.flags & P_REFOUT)) - return uerr("cannot search for crossed-endian models"); - - pass = 0; - do { - mptr = candmods = reveng(&model, qpoly, rflags, args, apolys); - 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) - pfree(qptr); - free(apolys); - if(~uflags & C_RESULT) - return uerr("no models found"); - mfree(&model); - } - return 1; -} - -//test call to GetModels -int CmdrevengTest(const char *Cmd){ - char *Models[80]; - int count = 0; - uint8_t widtharr[80] = {0}; - uint8_t width = 0; - width = param_get8(Cmd, 0); - //PrintAndLog("width: %d",width); - if (width > 89) - return uerr("Width cannot exceed 89"); - - widtharr[0] = width; - int ans = GetModels(Models, &count, widtharr); - if (!ans) return 0; - - PrintAndLog("Count: %d",count); - for (int i = 0; i < count; i++){ - PrintAndLog("Model %d: %s, width: %d",i,Models[i], widtharr[i]); - free(Models[i]); - } - return 1; -} - -//-c || -v -//inModel = valid model name string - CRC-8 -//inHexStr = input hex string to calculate crc on -//reverse = reverse calc option if true -//endian = {0 = calc default endian input and output, b = big endian input and output, B = big endian output, r = right justified -// l = little endian input and output, L = little endian output only, t = left justified} -//result = calculated crc hex string -int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *result){ - /* default values */ - static model_t model = { - PZERO, // no CRC polynomial, user must specify - PZERO, // Init = 0 - P_BE, // RefIn = false, RefOut = false, plus P_RTJUST setting in reveng.h - PZERO, // XorOut = 0 - PZERO, // check value unused - NULL // no model name - }; - int ibperhx = 8, obperhx = 8; - int rflags = 0; // search flags - int c; - //unsigned long width; - poly_t apoly, crc; - - char *string; - - // stdin must be binary - #ifdef _WIN32 - _setmode(STDIN_FILENO, _O_BINARY); - #endif /* _WIN32 */ - - SETBMP(); - //set model - if(!(c = mbynam(&model, inModel))) { - fprintf(stderr,"error: preset model '%s' not found. Use reveng -D to list presets.\n", inModel); - return 0; - } - if(c < 0) - return uerr("no preset models available"); - - // must set width so that parameter to -ipx is not zeroed - //width = plen(model.spoly); - rflags |= R_HAVEP | R_HAVEI | R_HAVERI | R_HAVERO | R_HAVEX; - - //set flags - switch (endian) { - case 'b': /* b big-endian (RefIn = false, RefOut = false ) */ - model.flags &= ~P_REFIN; - rflags |= R_HAVERI; - /* fall through: */ - case 'B': /* B big-endian output (RefOut = false) */ - model.flags &= ~P_REFOUT; - rflags |= R_HAVERO; - mnovel(&model); - /* fall through: */ - case 'r': /* r right-justified */ - model.flags |= P_RTJUST; - break; - case 'l': /* l little-endian input and output */ - model.flags |= P_REFIN; - rflags |= R_HAVERI; - /* fall through: */ - case 'L': /* L little-endian output */ - model.flags |= P_REFOUT; - rflags |= R_HAVERO; - mnovel(&model); - /* fall through: */ - case 't': /* t left-justified */ - model.flags &= ~P_RTJUST; - break; - } - - mcanon(&model); - - if (reverse) { - // v calculate reversed CRC - /* Distinct from the -V switch as this causes - * the arguments and output to be reversed as well. - */ - // reciprocate Poly - prcp(&model.spoly); - - /* mrev() does: - * if(refout) prev(init); else prev(xorout); - * but here the entire argument polynomial is - * reflected, not just the characters, so RefIn - * and RefOut are not inverted as with -V. - * Consequently Init is the mirror image of the - * one resulting from -V, and so we have: - */ - if(~model.flags & P_REFOUT) { - prev(&model.init); - prev(&model.xorout); - } - - // swap init and xorout - apoly = model.init; - model.init = model.xorout; - model.xorout = apoly; - } - // c calculate CRC - - // validate inputs - /* if(plen(model.spoly) == 0) { - * fprintf(stderr,"%s: no polynomial specified for -%c (add -w WIDTH -p POLY)\n", myname, mode); - * exit(EXIT_FAILURE); - * } - */ - - /* in the Williams model, xorout is applied after the refout stage. - * as refout is part of ptostr(), we reverse xorout here. - */ - if(model.flags & P_REFOUT) - prev(&model.xorout); - - apoly = strtop(inHexStr, model.flags, ibperhx); - - if(reverse) - prev(&apoly); - - crc = pcrc(apoly, model.spoly, model.init, model.xorout, model.flags); - - if(reverse) - prev(&crc); - - string = ptostr(crc, model.flags, obperhx); - for (int i = 0; i < 50; i++){ - result[i] = string[i]; - if (result[i]==0) break; - } - free(string); - pfree(&crc); - pfree(&apoly); - return 1; -} - -//test call to RunModel -int CmdrevengTestC(const char *Cmd){ - int cmdp = 0; - char inModel[30] = {0x00}; - char inHexStr[30] = {0x00}; - char result[30]; - int dataLen; - char endian = 0; - dataLen = param_getstr(Cmd, cmdp++, inModel); - if (dataLen < 4) return 0; - dataLen = param_getstr(Cmd, cmdp++, inHexStr); - if (dataLen < 4) return 0; - bool reverse = (param_get8(Cmd, cmdp++)) ? true : false; - endian = param_getchar(Cmd, cmdp++); - - //PrintAndLog("mod: %s, hex: %s, rev %d", inModel, inHexStr, reverse); - int ans = RunModel(inModel, inHexStr, reverse, endian, result); - if (!ans) return 0; - - PrintAndLog("Result: %s",result); - return 1; -} - -//returns a calloced string (needs to be freed) -char *SwapEndianStr(const char *inStr, const size_t len, const uint8_t blockSize){ - char *tmp = calloc(len+1, sizeof(char)); - 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)]; - tmp[i+(blockSize*block)+1] = inStr[(blockSize-1-i)+(blockSize*block)]; - } - } - return tmp; -} - -// takes hex string in and searches for a matching result (hex string must include checksum) -int CmdrevengSearch(const char *Cmd){ - char inHexStr[50] = {0x00}; - int dataLen = param_getstr(Cmd, 0, inHexStr); - if (dataLen < 4) return 0; - - char *Models[80]; - int count = 0; - uint8_t width[80]; - width[0] = 0; - uint8_t crcChars = 0; - char result[30]; - char revResult[30]; - int ans = GetModels(Models, &count, width); - bool found = false; - if (!ans) return 0; - - // try each model and get result - for (int i = 0; i < count; i++){ - /*if (found) { - free(Models[i]); - continue; - }*/ - // round up to # of characters in this model's crc - crcChars = ((width[i]+7)/8)*2; - // can't test a model that has more crc digits than our data - if (crcChars >= dataLen) - continue; - memset(result, 0, 30); - char *inCRC = calloc(crcChars+1, sizeof(char)); - memcpy(inCRC, inHexStr+(dataLen-crcChars), crcChars); - - char *outHex = calloc(dataLen-crcChars+1, sizeof(char)); - memcpy(outHex, inHexStr, dataLen-crcChars); - - //PrintAndLog("DEBUG: dataLen: %d, crcChars: %d, Model: %s, CRC: %s, width: %d, outHex: %s",dataLen, crcChars, Models[i], inCRC, width[i], outHex); - ans = RunModel(Models[i], outHex, false, 0, result); - if (ans) { - //test for match - if (memcmp(result, inCRC, crcChars)==0){ - PrintAndLog("\nFound a possible match!\nModel: %s\nValue: %s\n",Models[i], result); - //optional - stop searching if found... - found = true; - } else { - if (crcChars > 2){ - char *swapEndian = SwapEndianStr(result, crcChars, crcChars); - if (memcmp(swapEndian, inCRC, crcChars)==0){ - PrintAndLog("\nFound a possible match!\nModel: %s\nValue EndianSwapped: %s\n",Models[i], swapEndian); - //optional - stop searching if found... - found = true; - } - free(swapEndian); - } - } - } - - //if (!found){ - ans = RunModel(Models[i], outHex, true, 0, revResult); - if (ans) { - //test for match - if (memcmp(revResult, inCRC, crcChars)==0){ - PrintAndLog("\nFound a possible match!\nModel Reversed: %s\nValue: %s\n",Models[i], revResult); - //optional - stop searching if found... - found = true; - } else { - if (crcChars > 2){ - char *swapEndian = SwapEndianStr(revResult, crcChars, crcChars); - if (memcmp(swapEndian, inCRC, crcChars)==0){ - PrintAndLog("\nFound a possible match!\nModel Reversed: %s\nValue EndianSwapped: %s\n",Models[i], swapEndian); - //optional - stop searching if found... - found = true; - } - free(swapEndian); - } - } - } - //} - free(inCRC); - free(outHex); - free(Models[i]); - } - if (!found) PrintAndLog("\nNo matches found\n"); - return 1; -} diff --git a/client/cmdcrc.h b/client/cmdcrc.h deleted file mode 100644 index 5041554d..00000000 --- a/client/cmdcrc.h +++ /dev/null @@ -1,20 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2015 iceman -// -// This code is licensed to you under the terms of the GNU GPL, version 2 or, -// at your option, any later version. See the LICENSE.txt file for the text of -// the license. -//----------------------------------------------------------------------------- -// CRC Calculations from the software reveng commands -//----------------------------------------------------------------------------- - -#ifndef CMDCRC_H__ -#define CMDCRC_H__ - -int CmdCrc(const char *Cmd); -int CmdrevengTest(const char *Cmd); -int CmdrevengTestC(const char *Cmd); -int CmdrevengSearch(const char *Cmd); -int GetModels(char *Models[], int *count, uint8_t *width); -int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *result); -#endif diff --git a/client/cmddata.c b/client/cmddata.c index 1f548284..c4f0e8a3 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -8,15 +8,15 @@ // Data and Graph commands //----------------------------------------------------------------------------- +#include "cmddata.h" + #include // also included in util.h #include // also included in util.h #include #include // for CmdNorm INT_MIN && INT_MAX -#include "data.h" // also included in util.h -#include "cmddata.h" #include "util.h" #include "cmdmain.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" // for show graph controls #include "graph.h" // for graph data #include "cmdparser.h"// already included in cmdmain.h @@ -591,8 +591,7 @@ int CmdBitsamples(const char *Cmd) int cnt = 0; uint8_t got[12288]; - GetFromBigBuf(got,sizeof(got),0); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, sizeof(got), 0 , NULL, -1, false); for (int j = 0; j < sizeof(got); j++) { for (int k = 0; k < 8; k++) { @@ -1131,8 +1130,7 @@ int CmdHexsamples(const char *Cmd) return 0; } - GetFromBigBuf(got,requested,offset); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, requested, offset, NULL, -1, false); i = 0; for (j = 0; j < requested; j++) { @@ -1200,10 +1198,9 @@ int getSamples(int n, bool silent) n = sizeof(got); if (!silent) PrintAndLog("Reading %d bytes from device memory\n", n); - GetFromBigBuf(got,n,0); - if (!silent) PrintAndLog("Data fetched"); UsbCommand response; - WaitForResponse(CMD_ACK, &response); + GetFromBigBuf(got, n, 0, &response, -1, false); + if (!silent) PrintAndLog("Data fetched"); uint8_t bits_per_sample = 8; //Old devices without this feature would send 0 at arg[0] @@ -1281,26 +1278,36 @@ int CmdTuneSamples(const char *Cmd) peakf = resp.arg[2] & 0xffff; peakv = resp.arg[2] >> 16; PrintAndLog(""); - PrintAndLog("# LF antenna: %5.2f V @ 125.00 kHz", vLf125/1000.0); - PrintAndLog("# LF antenna: %5.2f V @ 134.00 kHz", vLf134/1000.0); - PrintAndLog("# LF optimal: %5.2f V @%9.2f kHz", peakv/1000.0, 12000.0/(peakf+1)); - PrintAndLog("# HF antenna: %5.2f V @ 13.56 MHz", vHf/1000.0); + if (arg & FLAG_TUNE_LF) + { + PrintAndLog("# LF antenna: %5.2f V @ 125.00 kHz", vLf125/500.0); + PrintAndLog("# LF antenna: %5.2f V @ 134.00 kHz", vLf134/500.0); + PrintAndLog("# LF optimal: %5.2f V @%9.2f kHz", peakv/500.0, 12000.0/(peakf+1)); + } + if (arg & FLAG_TUNE_HF) + PrintAndLog("# HF antenna: %5.2f V @ 13.56 MHz", vHf/1000.0); - #define LF_UNUSABLE_V 2948 // was 2000. Changed due to bugfix in voltage measurements. LF results are now 47% higher. - #define LF_MARGINAL_V 14739 // was 10000. Changed due to bugfix bug in voltage measurements. LF results are now 47% higher. - #define HF_UNUSABLE_V 3167 // was 2000. Changed due to bugfix in voltage measurements. HF results are now 58% higher. - #define HF_MARGINAL_V 7917 // was 5000. Changed due to bugfix in voltage measurements. HF results are now 58% higher. + #define LF_UNUSABLE_V 3000 + #define LF_MARGINAL_V 15000 + #define HF_UNUSABLE_V 3200 + #define HF_MARGINAL_V 8000 - if (peakv < LF_UNUSABLE_V) - PrintAndLog("# Your LF antenna is unusable."); - else if (peakv < LF_MARGINAL_V) - PrintAndLog("# Your LF antenna is marginal."); - if (vHf < HF_UNUSABLE_V) - PrintAndLog("# Your HF antenna is unusable."); - else if (vHf < HF_MARGINAL_V) - PrintAndLog("# Your HF antenna is marginal."); + if (arg & FLAG_TUNE_LF) + { + if (peakv<<1 < LF_UNUSABLE_V) + PrintAndLog("# Your LF antenna is unusable."); + else if (peakv<<1 < LF_MARGINAL_V) + PrintAndLog("# Your LF antenna is marginal."); + } + if (arg & FLAG_TUNE_HF) + { + if (vHf < HF_UNUSABLE_V) + PrintAndLog("# Your HF antenna is unusable."); + else if (vHf < HF_MARGINAL_V) + PrintAndLog("# Your HF antenna is marginal."); + } - if (peakv >= LF_UNUSABLE_V) { + if (peakv<<1 >= LF_UNUSABLE_V) { for (int i = 0; i < 256; i++) { GraphBuffer[i] = resp.d.asBytes[i] - 128; } diff --git a/client/cmdhf.c b/client/cmdhf.c index eb2ba9b2..762cc791 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh +// Merlok - 2017 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -8,18 +9,18 @@ // High frequency commands //----------------------------------------------------------------------------- +#include "cmdhf.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "util.h" -#include "data.h" #include "ui.h" #include "iso14443crc.h" #include "parity.h" #include "cmdmain.h" #include "cmdparser.h" -#include "cmdhf.h" #include "cmdhf14a.h" #include "cmdhf14b.h" #include "cmdhf15.h" @@ -27,9 +28,12 @@ #include "cmdhflegic.h" #include "cmdhficlass.h" #include "cmdhfmf.h" +#include "cmdhfmfp.h" #include "cmdhfmfu.h" #include "cmdhftopaz.h" #include "protocols.h" +#include "emv/cmdemv.h" +#include "cmdhflist.h" static int CmdHelp(const char *Cmd); @@ -40,236 +44,6 @@ int CmdHFTune(const char *Cmd) return 0; } - -void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - switch(cmd[0]) - { - case ISO14443A_CMD_WUPA: snprintf(exp,size,"WUPA"); break; - case ISO14443A_CMD_ANTICOLL_OR_SELECT:{ - // 93 20 = Anticollision (usage: 9320 - answer: 4bytes UID+1byte UID-bytes-xor) - // 93 70 = Select (usage: 9370+5bytes 9320 answer - answer: 1byte SAK) - if(cmd[1] == 0x70) - { - snprintf(exp,size,"SELECT_UID"); break; - }else - { - snprintf(exp,size,"ANTICOLL"); break; - } - } - case ISO14443A_CMD_ANTICOLL_OR_SELECT_2:{ - //95 20 = Anticollision of cascade level2 - //95 70 = Select of cascade level2 - if(cmd[2] == 0x70) - { - snprintf(exp,size,"SELECT_UID-2"); break; - }else - { - snprintf(exp,size,"ANTICOLL-2"); break; - } - } - case ISO14443A_CMD_REQA: snprintf(exp,size,"REQA"); break; - case ISO14443A_CMD_READBLOCK: snprintf(exp,size,"READBLOCK(%d)",cmd[1]); break; - case ISO14443A_CMD_WRITEBLOCK: snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); break; - case ISO14443A_CMD_HALT: snprintf(exp,size,"HALT"); break; - case ISO14443A_CMD_RATS: snprintf(exp,size,"RATS"); break; - case MIFARE_CMD_INC: snprintf(exp,size,"INC(%d)",cmd[1]); break; - case MIFARE_CMD_DEC: snprintf(exp,size,"DEC(%d)",cmd[1]); break; - case MIFARE_CMD_RESTORE: snprintf(exp,size,"RESTORE(%d)",cmd[1]); break; - case MIFARE_CMD_TRANSFER: snprintf(exp,size,"TRANSFER(%d)",cmd[1]); break; - case MIFARE_AUTH_KEYA:{ - if ( cmdsize > 3) - snprintf(exp,size,"AUTH-A(%d)",cmd[1]); - else - // case MIFARE_ULEV1_VERSION : both 0x60. - snprintf(exp,size,"EV1 VERSION"); - break; - } - case MIFARE_AUTH_KEYB: snprintf(exp,size,"AUTH-B(%d)",cmd[1]); break; - case MIFARE_MAGICWUPC1: snprintf(exp,size,"MAGIC WUPC1"); break; - case MIFARE_MAGICWUPC2: snprintf(exp,size,"MAGIC WUPC2"); break; - case MIFARE_MAGICWIPEC: snprintf(exp,size,"MAGIC WIPEC"); break; - case MIFARE_ULC_AUTH_1: snprintf(exp,size,"AUTH "); break; - case MIFARE_ULC_AUTH_2: snprintf(exp,size,"AUTH_ANSW"); break; - case MIFARE_ULEV1_AUTH: - if ( cmdsize == 7 ) - snprintf(exp,size,"PWD-AUTH KEY: 0x%02x%02x%02x%02x", cmd[1], cmd[2], cmd[3], cmd[4] ); - else - snprintf(exp,size,"PWD-AUTH"); - break; - case MIFARE_ULEV1_FASTREAD:{ - if ( cmdsize >=3 && cmd[2] <= 0xE6) - snprintf(exp,size,"READ RANGE (%d-%d)",cmd[1],cmd[2]); - else - snprintf(exp,size,"?"); - break; - } - case MIFARE_ULC_WRITE:{ - if ( cmd[1] < 0x21 ) - snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); - else - snprintf(exp,size,"?"); - break; - } - case MIFARE_ULEV1_READ_CNT:{ - if ( cmd[1] < 5 ) - snprintf(exp,size,"READ CNT(%d)",cmd[1]); - else - snprintf(exp,size,"?"); - break; - } - case MIFARE_ULEV1_INCR_CNT:{ - if ( cmd[1] < 5 ) - snprintf(exp,size,"INCR(%d)",cmd[1]); - else - snprintf(exp,size,"?"); - break; - } - case MIFARE_ULEV1_READSIG: snprintf(exp,size,"READ_SIG"); break; - case MIFARE_ULEV1_CHECKTEAR: snprintf(exp,size,"CHK_TEARING(%d)",cmd[1]); break; - case MIFARE_ULEV1_VCSL: snprintf(exp,size,"VCSL"); break; - default: snprintf(exp,size,"?"); break; - } - return; -} - -void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - switch(cmd[0]) - { - case ICLASS_CMD_ACTALL: snprintf(exp,size,"ACTALL"); break; - case ICLASS_CMD_READ_OR_IDENTIFY:{ - if(cmdsize > 1){ - snprintf(exp,size,"READ(%d)",cmd[1]); - }else{ - snprintf(exp,size,"IDENTIFY"); - } - break; - } - case ICLASS_CMD_SELECT: snprintf(exp,size,"SELECT"); break; - case ICLASS_CMD_PAGESEL: snprintf(exp,size,"PAGESEL(%d)", cmd[1]); break; - case ICLASS_CMD_READCHECK_KC:snprintf(exp,size,"READCHECK[Kc](%d)", cmd[1]); break; - case ICLASS_CMD_READCHECK_KD:snprintf(exp,size,"READCHECK[Kd](%d)", cmd[1]); break; - case ICLASS_CMD_CHECK: snprintf(exp,size,"CHECK"); break; - case ICLASS_CMD_DETECT: snprintf(exp,size,"DETECT"); break; - case ICLASS_CMD_HALT: snprintf(exp,size,"HALT"); break; - case ICLASS_CMD_UPDATE: snprintf(exp,size,"UPDATE(%d)",cmd[1]); break; - case ICLASS_CMD_ACT: snprintf(exp,size,"ACT"); break; - case ICLASS_CMD_READ4: snprintf(exp,size,"READ4(%d)",cmd[1]); break; - default: snprintf(exp,size,"?"); break; - } - return; -} - -void annotateIso15693(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - - if(cmd[0] == 0x26) - { - switch(cmd[1]){ - case ISO15693_INVENTORY :snprintf(exp, size, "INVENTORY");break; - case ISO15693_STAYQUIET :snprintf(exp, size, "STAY_QUIET");break; - default: snprintf(exp,size,"?"); break; - - } - }else if(cmd[0] == 0x02) - { - switch(cmd[1]) - { - case ISO15693_READBLOCK :snprintf(exp, size, "READBLOCK");break; - case ISO15693_WRITEBLOCK :snprintf(exp, size, "WRITEBLOCK");break; - case ISO15693_LOCKBLOCK :snprintf(exp, size, "LOCKBLOCK");break; - case ISO15693_READ_MULTI_BLOCK :snprintf(exp, size, "READ_MULTI_BLOCK");break; - case ISO15693_SELECT :snprintf(exp, size, "SELECT");break; - case ISO15693_RESET_TO_READY :snprintf(exp, size, "RESET_TO_READY");break; - case ISO15693_WRITE_AFI :snprintf(exp, size, "WRITE_AFI");break; - case ISO15693_LOCK_AFI :snprintf(exp, size, "LOCK_AFI");break; - case ISO15693_WRITE_DSFID :snprintf(exp, size, "WRITE_DSFID");break; - case ISO15693_LOCK_DSFID :snprintf(exp, size, "LOCK_DSFID");break; - case ISO15693_GET_SYSTEM_INFO :snprintf(exp, size, "GET_SYSTEM_INFO");break; - case ISO15693_READ_MULTI_SECSTATUS :snprintf(exp, size, "READ_MULTI_SECSTATUS");break; - default: snprintf(exp,size,"?"); break; - } - } -} - - -void annotateTopaz(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - switch(cmd[0]) { - case TOPAZ_REQA :snprintf(exp, size, "REQA");break; - case TOPAZ_WUPA :snprintf(exp, size, "WUPA");break; - case TOPAZ_RID :snprintf(exp, size, "RID");break; - case TOPAZ_RALL :snprintf(exp, size, "RALL");break; - case TOPAZ_READ :snprintf(exp, size, "READ");break; - case TOPAZ_WRITE_E :snprintf(exp, size, "WRITE-E");break; - case TOPAZ_WRITE_NE :snprintf(exp, size, "WRITE-NE");break; - case TOPAZ_RSEG :snprintf(exp, size, "RSEG");break; - case TOPAZ_READ8 :snprintf(exp, size, "READ8");break; - case TOPAZ_WRITE_E8 :snprintf(exp, size, "WRITE-E8");break; - case TOPAZ_WRITE_NE8 :snprintf(exp, size, "WRITE-NE8");break; - default: snprintf(exp,size,"?"); break; - } -} - - -/** -06 00 = INITIATE -0E xx = SELECT ID (xx = Chip-ID) -0B = Get UID -08 yy = Read Block (yy = block number) -09 yy dd dd dd dd = Write Block (yy = block number; dd dd dd dd = data to be written) -0C = Reset to Inventory -0F = Completion -0A 11 22 33 44 55 66 = Authenticate (11 22 33 44 55 66 = data to authenticate) -**/ - -void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ - switch(cmd[0]){ - case ISO14443B_REQB : snprintf(exp,size,"REQB");break; - case ISO14443B_ATTRIB : snprintf(exp,size,"ATTRIB");break; - case ISO14443B_HALT : snprintf(exp,size,"HALT");break; - case ISO14443B_INITIATE : snprintf(exp,size,"INITIATE");break; - case ISO14443B_SELECT : snprintf(exp,size,"SELECT(%d)",cmd[1]);break; - case ISO14443B_GET_UID : snprintf(exp,size,"GET UID");break; - case ISO14443B_READ_BLK : snprintf(exp,size,"READ_BLK(%d)", cmd[1]);break; - case ISO14443B_WRITE_BLK : snprintf(exp,size,"WRITE_BLK(%d)",cmd[1]);break; - case ISO14443B_RESET : snprintf(exp,size,"RESET");break; - case ISO14443B_COMPLETION : snprintf(exp,size,"COMPLETION");break; - case ISO14443B_AUTHENTICATE : snprintf(exp,size,"AUTHENTICATE");break; - default : snprintf(exp,size ,"?");break; - } - -} - -/** - * @brief iso14443A_CRC_check Checks CRC in command or response - * @param isResponse - * @param data - * @param len - * @return 0 : CRC-command, CRC not ok - * 1 : CRC-command, CRC ok - * 2 : Not crc-command - */ - -uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len) -{ - uint8_t b1,b2; - - if(len <= 2) return 2; - - if(isResponse & (len < 6)) return 2; - - ComputeCrc14443(CRC_14443_A, data, len-2, &b1, &b2); - if (b1 != data[len-2] || b2 != data[len-1]) { - return 0; - } else { - return 1; - } -} - - /** * @brief iso14443B_CRC_check Checks CRC in command or response * @param isResponse @@ -417,6 +191,8 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui uint8_t topaz_reader_command[9]; uint32_t timestamp, first_timestamp, EndOfTransmissionTimestamp; char explanation[30] = {0}; + uint8_t mfData[32] = {0}; + size_t mfDataLen = 0; if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) > traceLen) return traceLen; @@ -465,6 +241,9 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui case TOPAZ: crcStatus = iso14443B_CRC_check(isResponse, frame, data_len); break; + case PROTO_MIFARE: + crcStatus = mifare_CRC_check(isResponse, frame, data_len); + break; case ISO_14443A: crcStatus = iso14443A_CRC_check(isResponse, frame, data_len); break; @@ -513,6 +292,9 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui EndOfTransmissionTimestamp = timestamp + duration; + if (protocol == PROTO_MIFARE) + annotateMifare(explanation, sizeof(explanation), frame, data_len, parityBytes, parity_len, isResponse); + if(!isResponse) { switch(protocol) { @@ -541,12 +323,25 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui (j == num_lines-1) ? explanation : ""); } } + + if (DecodeMifareData(frame, data_len, parityBytes, isResponse, mfData, &mfDataLen)) { + memset(explanation, 0x00, sizeof(explanation)); + if (!isResponse) { + explanation[0] = '>'; + annotateIso14443a(&explanation[1], sizeof(explanation) - 1, mfData, mfDataLen); + } + uint8_t crcc = iso14443A_CRC_check(isResponse, mfData, mfDataLen); + PrintAndLog(" | * | dec |%-64s | %-4s| %s", + sprint_hex(mfData, mfDataLen), + (crcc == 0 ? "!crc" : (crcc == 1 ? " ok " : " ")), + (true) ? explanation : ""); + }; if (is_last_record(tracepos, trace, traceLen)) return traceLen; if (showWaitCycles && !isResponse && next_record_is_response(tracepos, trace)) { uint32_t next_timestamp = *((uint32_t *)(trace + tracepos)); - PrintAndLog(" %9d | %9d | %s | fdt (Frame Delay Time): %d", + PrintAndLog(" %10d | %10d | %s | fdt (Frame Delay Time): %d", (EndOfTransmissionTimestamp - first_timestamp), (next_timestamp - first_timestamp), " ", @@ -559,101 +354,194 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui int CmdHFList(const char *Cmd) { + #ifdef WITH_SMARTCARD + PrintAndLog("TEST_WITH_SMARTCARD"); + #endif + #ifdef WITH_TEST + PrintAndLog("TEST_WITH_TEST"); + #endif bool showWaitCycles = false; bool markCRCBytes = false; + bool loadFromFile = false; + bool saveToFile = false; + char param1 = '\0'; + char param2 = '\0'; + char param3 = '\0'; char type[40] = {0}; - int tlen = param_getstr(Cmd,0,type); - char param1 = param_getchar(Cmd, 1); - char param2 = param_getchar(Cmd, 2); - bool errors = false; + char filename[FILE_PATH_SIZE] = {0}; uint8_t protocol = 0; - //Validate params + + // parse command line + int tlen = param_getstr(Cmd, 0, type, sizeof(type)); + if (param_getlength(Cmd, 1) == 1) { + param1 = param_getchar(Cmd, 1); + } else { + param_getstr(Cmd, 1, filename, sizeof(filename)); + } + if (param_getlength(Cmd, 2) == 1) { + param2 = param_getchar(Cmd, 2); + } else if (strlen(filename) == 0) { + param_getstr(Cmd, 2, filename, sizeof(filename)); + } + if (param_getlength(Cmd, 3) == 1) { + param3 = param_getchar(Cmd, 3); + } else if (strlen(filename) == 0) { + param_getstr(Cmd, 3, filename, sizeof(filename)); + } + + // Validate param1 + bool errors = false; if(tlen == 0) { errors = true; } if(param1 == 'h' - || (param1 != 0 && param1 != 'f' && param1 != 'c') - || (param2 != 0 && param2 != 'f' && param2 != 'c')) { + || (param1 != 0 && param1 != 'f' && param1 != 'c' && param1 != 'l') + || (param2 != 0 && param2 != 'f' && param2 != 'c' && param2 != 'l') + || (param3 != 0 && param3 != 'f' && param3 != 'c' && param3 != 'l')) { errors = true; } if(!errors) { if(strcmp(type, "iclass") == 0) { protocol = ICLASS; + } else if(strcmp(type, "mf") == 0) { + protocol = PROTO_MIFARE; } else if(strcmp(type, "14a") == 0) { protocol = ISO_14443A; } else if(strcmp(type, "14b") == 0) { protocol = ISO_14443B; - } else if(strcmp(type,"topaz")== 0) { + } else if(strcmp(type,"topaz") == 0) { protocol = TOPAZ; - } else if(strcmp(type,"raw")== 0) { - protocol = -1;//No crc, no annotations + } else if(strcmp(type,"raw") == 0) { + protocol = -1; //No crc, no annotations + } else if (strcmp(type, "save") == 0) { + saveToFile = true; } else { errors = true; } } + + if (param1 == 'f' || param2 == 'f' || param3 == 'f') { + showWaitCycles = true; + } + if (param1 == 'c' || param2 == 'c' || param3 == 'c') { + markCRCBytes = true; + } + + if (param1 == 'l' || param2 == 'l' || param3 == 'l') { + loadFromFile = true; + } + + if ((loadFromFile || saveToFile) && strlen(filename) == 0) { + errors = true; + } + + if (loadFromFile && saveToFile) { + errors = true; + } + if (errors) { - PrintAndLog("List protocol data in trace buffer."); - PrintAndLog("Usage: hf list [f][c]"); + PrintAndLog("List or save protocol data."); + PrintAndLog("Usage: hf list [f] [c] [l ]"); + PrintAndLog(" hf list save "); PrintAndLog(" f - show frame delay times as well"); PrintAndLog(" c - mark CRC bytes"); + PrintAndLog(" l - load data from file instead of trace buffer"); + PrintAndLog(" save - save data to file"); PrintAndLog("Supported values:"); PrintAndLog(" raw - just show raw data without annotations"); PrintAndLog(" 14a - interpret data as iso14443a communications"); + PrintAndLog(" mf - interpret data as iso14443a communications and decrypt crypto1 stream"); PrintAndLog(" 14b - interpret data as iso14443b communications"); PrintAndLog(" iclass - interpret data as iclass communications"); PrintAndLog(" topaz - interpret data as topaz communications"); PrintAndLog(""); PrintAndLog("example: hf list 14a f"); PrintAndLog("example: hf list iclass"); + PrintAndLog("example: hf list save myCardTrace.trc"); + PrintAndLog("example: hf list 14a l myCardTrace.trc"); return 0; } - if (param1 == 'f' || param2 == 'f') { - showWaitCycles = true; - } - - if (param1 == 'c' || param2 == 'c') { - markCRCBytes = true; - } - uint8_t *trace; - uint16_t tracepos = 0; - trace = malloc(USB_CMD_DATA_SIZE); - - // Query for the size of the trace - UsbCommand response; - GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0); - WaitForResponse(CMD_ACK, &response); - uint16_t traceLen = response.arg[2]; - if (traceLen > USB_CMD_DATA_SIZE) { - uint8_t *p = realloc(trace, traceLen); - if (p == NULL) { + uint32_t tracepos = 0; + uint32_t traceLen = 0; + + if (loadFromFile) { + #define TRACE_CHUNK_SIZE (1<<16) // 64K to start with. Will be enough for BigBuf and some room for future extensions + FILE *tracefile = NULL; + size_t bytes_read; + trace = malloc(TRACE_CHUNK_SIZE); + if (trace == NULL) { PrintAndLog("Cannot allocate memory for trace"); - free(trace); return 2; } - trace = p; - GetFromBigBuf(trace, traceLen, 0); - WaitForResponse(CMD_ACK, NULL); + if ((tracefile = fopen(filename,"rb")) == NULL) { + PrintAndLog("Could not open file %s", filename); + free(trace); + return 0; + } + while (!feof(tracefile)) { + bytes_read = fread(trace+traceLen, 1, TRACE_CHUNK_SIZE, tracefile); + traceLen += bytes_read; + if (!feof(tracefile)) { + uint8_t *p = realloc(trace, traceLen + TRACE_CHUNK_SIZE); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + fclose(tracefile); + return 2; + } + trace = p; + } + } + fclose(tracefile); + } else { + trace = malloc(USB_CMD_DATA_SIZE); + // Query for the size of the trace + UsbCommand response; + GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, -1, false); + traceLen = response.arg[2]; + if (traceLen > USB_CMD_DATA_SIZE) { + uint8_t *p = realloc(trace, traceLen); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + return 2; + } + trace = p; + GetFromBigBuf(trace, traceLen, 0, NULL, -1, false); + } } - - PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); - PrintAndLog(""); - PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); - PrintAndLog("iso14443a - All times are in carrier periods (1/13.56Mhz)"); - PrintAndLog("iClass - Timings are not as accurate"); - PrintAndLog(""); - PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); - PrintAndLog("------------|------------|-----|-----------------------------------------------------------------|-----|--------------------|"); - while(tracepos < traceLen) - { - tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles, markCRCBytes); + if (saveToFile) { + FILE *tracefile = NULL; + if ((tracefile = fopen(filename,"wb")) == NULL) { + PrintAndLog("Could not create file %s", filename); + return 1; + } + fwrite(trace, 1, traceLen, tracefile); + PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, filename); + fclose(tracefile); + } else { + PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); + PrintAndLog(""); + PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); + PrintAndLog("iso14443a - All times are in carrier periods (1/13.56Mhz)"); + PrintAndLog("iClass - Timings are not as accurate"); + PrintAndLog(""); + PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); + PrintAndLog("------------|------------|-----|-----------------------------------------------------------------|-----|--------------------|"); + + ClearAuthData(); + while(tracepos < traceLen) + { + tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles, markCRCBytes); + } } free(trace); @@ -663,7 +551,7 @@ int CmdHFList(const char *Cmd) int CmdHFSearch(const char *Cmd){ int ans = 0; PrintAndLog(""); - ans = CmdHF14AReader("s"); + ans = CmdHF14AInfo("s"); if (ans > 0) { PrintAndLog("\nValid ISO14443A Tag Found - Quiting Search\n"); return ans; @@ -703,10 +591,12 @@ static command_t CommandTable[] = {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, {"epa", CmdHFEPA, 1, "{ German Identification Card... }"}, + {"emv", CmdHFEMV, 1, "{ EMV cards... }"}, {"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"}, {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, {"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"}, {"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"}, + {"mfp", CmdHFMFP, 1, "{ MIFARE Plus RFIDs... }"}, {"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"}, {"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"}, {"list", CmdHFList, 1, "List protocol data in trace buffer"}, diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index b75215a0..326eaf50 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2011, Merlok +// 2011, 2017 Merlok // Copyright (C) 2010 iZsh , Hagen Fritsch // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -9,27 +9,31 @@ // High frequency ISO14443A commands //----------------------------------------------------------------------------- +#include "cmdhf14a.h" + #include #include #include #include #include +#include #include "util.h" #include "util_posix.h" #include "iso14443crc.h" -#include "data.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" -#include "cmdhf14a.h" #include "common.h" #include "cmdmain.h" #include "mifare.h" #include "cmdhfmfu.h" #include "mifarehost.h" +#include "cliparser/cliparser.h" +#include "emv/apduinfo.h" +#include "emv/emvcore.h" static int CmdHelp(const char *Cmd); -static void waitCmd(uint8_t iLen); +static int waitCmd(uint8_t iLen); // structure and database for uid -> tagtype lookups typedef struct { @@ -37,7 +41,7 @@ typedef struct { char* desc; } manufactureName; -const manufactureName manufactureMapping[] = { +static const manufactureName manufactureMapping[] = { // ID, "Vendor Country" { 0x01, "Motorola UK" }, { 0x02, "ST Microelectronics SA France" }, @@ -110,7 +114,6 @@ const manufactureName manufactureMapping[] = { { 0x00, "no tag-info available" } // must be the last entry }; - // get a product description based on the UID // uid[8] tag uid // returns description of the best match @@ -133,7 +136,117 @@ int CmdHF14AList(const char *Cmd) return 0; } -int CmdHF14AReader(const char *Cmd) +int Hf14443_4aGetCardData(iso14a_card_select_t * card) { + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + memcpy(card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + if(select_status == 0) { + PrintAndLog("E->iso14443a card select failed"); + return 1; + } + + if(select_status == 2) { + PrintAndLog("E->Card doesn't support iso14443-4 mode"); + return 1; + } + + if(select_status == 3) { + PrintAndLog("E->Card doesn't support standard iso14443-3 anticollision"); + PrintAndLog("\tATQA : %02x %02x", card->atqa[1], card->atqa[0]); + return 1; + } + + PrintAndLog(" UID: %s", sprint_hex(card->uid, card->uidlen)); + PrintAndLog("ATQA: %02x %02x", card->atqa[1], card->atqa[0]); + PrintAndLog(" SAK: %02x [%" PRIu64 "]", card->sak, resp.arg[0]); + if(card->ats_len < 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes + PrintAndLog("E-> Error ATS length(%d) : %s", card->ats_len, sprint_hex(card->ats, card->ats_len)); + return 1; + } + PrintAndLog(" ATS: %s", sprint_hex(card->ats, card->ats_len)); + + return 0; +} + +int CmdHF14AReader(const char *Cmd) { + uint32_t cm = ISO14A_CONNECT; + bool leaveSignalON = false; + + CLIParserInit("hf 14a reader", "Executes ISO1443A anticollision-select group of commands.", NULL); + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep the field active after command executed"), + arg_lit0("xX", "drop", "just drop the signal field"), + arg_lit0("3", NULL, "ISO14443-3 select only (skip RATS)"), + arg_param_end + }; + if (CLIParserParseString(Cmd, argtable, arg_getsize(argtable), true)){ + CLIParserFree(); + return 0; + } + + leaveSignalON = arg_get_lit(1); + if (arg_get_lit(2)) { + cm = cm - ISO14A_CONNECT; + } + if (arg_get_lit(3)) { + cm |= ISO14A_NO_RATS; + } + + CLIParserFree(); + + if (leaveSignalON) + cm |= ISO14A_NO_DISCONNECT; + + UsbCommand c = {CMD_READER_ISO_14443a, {cm, 0, 0}}; + SendCommand(&c); + + if (ISO14A_CONNECT & cm) { + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + if(select_status == 0) { + PrintAndLog("iso14443a card select failed"); + return 1; + } + + if(select_status == 3) { + PrintAndLog("Card doesn't support standard iso14443-3 anticollision"); + PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); + return 1; + } + + PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); + PrintAndLog(" SAK : %02x [%" PRIu64 "]", card.sak, resp.arg[0]); + if(card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes + PrintAndLog(" ATS : %s", sprint_hex(card.ats, card.ats_len)); + } + if (leaveSignalON) { + PrintAndLog("Card is selected. You can now start sending commands"); + } + } + + if (!leaveSignalON) { + PrintAndLog("Field dropped."); + } + + return 0; +} + +int CmdHF14AInfo(const char *Cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; SendCommand(&c); @@ -169,10 +282,12 @@ int CmdHF14AReader(const char *Cmd) PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen)); PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); - PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); + PrintAndLog(" SAK : %02x [%" PRIu64 "]", card.sak, resp.arg[0]); + bool isMifareClassic = true; switch (card.sak) { case 0x00: + isMifareClassic = false; //***************************************test**************** // disconnect @@ -405,27 +520,21 @@ int CmdHF14AReader(const char *Cmd) // try to see if card responses to "chinese magic backdoor" commands. - c.cmd = CMD_MIFARE_CIDENT; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - WaitForResponse(CMD_ACK,&resp); + (void)mfCIdentify(); - uint8_t isGeneration = resp.arg[0] & 0xff; - switch( isGeneration ){ - case 1: PrintAndLog("Answers to chinese magic backdoor commands (GEN 1a): YES"); break; - case 2: PrintAndLog("Answers to chinese magic backdoor commands (GEN 1b): YES"); break; - default: PrintAndLog("Answers to chinese magic backdoor commands: NO"); break; + if (isMifareClassic) { + switch(DetectClassicPrng()) { + case 0: + PrintAndLog("Prng detection: HARDENED (hardnested)"); + break; + case 1: + PrintAndLog("Prng detection: WEAK"); + break; + default: + PrintAndLog("Prng detection error."); + } } - // disconnect - c.cmd = CMD_READER_ISO_14443a; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - return select_status; } @@ -442,7 +551,7 @@ int CmdHF14ACUIDs(const char *Cmd) // repeat n times for (int i = 0; i < n; i++) { // execute anticollision procedure - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}}; + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0}}; SendCommand(&c); UsbCommand resp; @@ -573,6 +682,236 @@ int CmdHF14ASnoop(const char *Cmd) { return 0; } +void DropField() { + UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; + SendCommand(&c); +} + +int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + static bool responseNum = false; + uint16_t cmdc = 0; + *dataoutlen = 0; + + if (activateField) { + responseNum = false; + UsbCommand resp; + + // Anticollision + SELECT card + UsbCommand ca = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT | ISO14A_CLEAR_TRACE, 0, 0}}; + SendCommand(&ca); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + PrintAndLog("14aRAW ERROR: Proxmark connection timeout."); + return 1; + } + + // check result + if (resp.arg[0] == 0) { + PrintAndLog("14aRAW ERROR: No card in field."); + return 1; + } + + if (resp.arg[0] != 1 && resp.arg[0] != 2) { + PrintAndLog("14aRAW ERROR: card not in iso14443-4. res=%d.", resp.arg[0]); + return 1; + } + + if (resp.arg[0] == 2) { // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + // get ATS + UsbCommand cr = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0}}; + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + memcpy(cr.d.asBytes, rats, 2); + SendCommand(&cr); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + PrintAndLog("14aRAW ERROR: Proxmark connection timeout."); + return 1; + } + + if (resp.arg[0] <= 0) { // ats_len + PrintAndLog("14aRAW ERROR: Can't get ATS."); + return 1; + } + } + } + + if (leaveSignalON) + cmdc |= ISO14A_NO_DISCONNECT; + + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_APPEND_CRC | cmdc, (datainlen & 0xFFFF) + 2, 0}}; + uint8_t header[] = {0x0a | responseNum, 0x00}; + responseNum ^= 1; + memcpy(c.d.asBytes, header, 2); + memcpy(&c.d.asBytes[2], datain, datainlen); + SendCommand(&c); + + uint8_t *recv; + UsbCommand resp; + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + recv = resp.d.asBytes; + int iLen = resp.arg[0]; + + if(!iLen) { + PrintAndLog("14aRAW ERROR: No card response."); + return 1; + } + + *dataoutlen = iLen - 2; + if (*dataoutlen < 0) + *dataoutlen = 0; + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLog("14aRAW ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); + return 2; + } + + if (recv[0] != header[0]) { + PrintAndLog("14aRAW ERROR: iso14443-4 framing error. Card send %2x must be %2x", dataout[0], header[0]); + return 2; + } + + memcpy(dataout, &recv[2], *dataoutlen); + + // CRC Check + if (iLen == -1) { + PrintAndLog("14aRAW ERROR: ISO 14443A CRC error."); + return 3; + } + + + } else { + PrintAndLog("14aRAW ERROR: Reply timeout."); + return 4; + } + + return 0; +} + +int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + uint16_t cmdc = 0; + + if (activateField) { + cmdc |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE; + } + if (leaveSignalON) + cmdc |= ISO14A_NO_DISCONNECT; + + // "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 USB_CMD_DATA_SIZE=512 + // timeout must be authomatically set by "get ATS" + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | cmdc, (datainlen & 0xFFFF), 0}}; + memcpy(c.d.asBytes, datain, datainlen); + SendCommand(&c); + + uint8_t *recv; + UsbCommand resp; + + if (activateField) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + PrintAndLog("APDU ERROR: Proxmark connection timeout."); + return 1; + } + if (resp.arg[0] != 1) { + PrintAndLog("APDU ERROR: Proxmark error %d.", resp.arg[0]); + return 1; + } + } + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + recv = resp.d.asBytes; + int iLen = resp.arg[0]; + + *dataoutlen = iLen - 2; + if (*dataoutlen < 0) + *dataoutlen = 0; + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLog("APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); + return 2; + } + + memcpy(dataout, recv, *dataoutlen); + + if(!iLen) { + PrintAndLog("APDU ERROR: No APDU response."); + return 1; + } + + // check block TODO + if (iLen == -2) { + PrintAndLog("APDU ERROR: Block type mismatch."); + return 2; + } + + // CRC Check + if (iLen == -1) { + PrintAndLog("APDU ERROR: ISO 14443A CRC error."); + return 3; + } + + // check apdu length + if (iLen < 4) { + PrintAndLog("APDU ERROR: Small APDU response. Len=%d", iLen); + return 2; + } + + } else { + PrintAndLog("APDU ERROR: Reply timeout."); + return 4; + } + + return 0; +} + +// ISO14443-4. 7. Half-duplex block transmission protocol +int CmdHF14AAPDU(const char *cmd) { + uint8_t data[USB_CMD_DATA_SIZE]; + int datalen = 0; + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; + + CLIParserInit("hf 14a apdu", + "Sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL)", + "Sample:\n\thf 14a apdu -st 00A404000E325041592E5359532E444446303100\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "leave the signal field ON after receive response"), + arg_lit0("tT", "tlv", "executes TLV decoder if it possible"), + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + activateField = arg_get_lit(1); + leaveSignalON = arg_get_lit(2); + decodeTLV = arg_get_lit(3); + // len = data + PCB(1b) + CRC(2b) + CLIGetHexBLessWithReturn(4, data, &datalen, 1 + 2); + + + CLIParserFree(); +// PrintAndLog("---str [%d] %s", arg_get_str(4)->count, arg_get_str(4)->sval[0]); + PrintAndLog(">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); + + int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, USB_CMD_DATA_SIZE, &datalen); + + if (res) + return res; + + PrintAndLog("<<<< %s", sprint_hex(data, datalen)); + + PrintAndLog("APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); + + // TLV decoder + if (decodeTLV && datalen > 4) { + TLVPrintFromBuffer(data, datalen - 2); + } + + return 0; +} int CmdHF14ACmdRaw(const char *cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; @@ -581,102 +920,62 @@ int CmdHF14ACmdRaw(const char *cmd) { bool power = false; bool active = false; bool active_select = false; + bool no_rats = false; uint16_t numbits = 0; bool bTimeout = false; uint32_t timeout = 0; bool topazmode = false; - char buf[5]=""; - int i = 0; uint8_t data[USB_CMD_DATA_SIZE]; - uint16_t datalen = 0; - uint32_t temp; + int datalen = 0; - if (strlen(cmd)<2) { - PrintAndLog("Usage: hf 14a raw [-r] [-c] [-p] [-f] [-b] [-t] <0A 0B 0C ... hex>"); - PrintAndLog(" -r do not read response"); - PrintAndLog(" -c calculate and append CRC"); - PrintAndLog(" -p leave the signal field ON after receive"); - PrintAndLog(" -a active signal field ON without select"); - PrintAndLog(" -s active signal field ON with select"); - PrintAndLog(" -b number of bits to send. Useful for send partial byte"); - PrintAndLog(" -t timeout in ms"); - PrintAndLog(" -T use Topaz protocol to send command"); + // extract parameters + CLIParserInit("hf 14a raw", "Send raw hex data to tag", + "Sample:\n"\ + "\thf 14a raw -pa -b7 -t1000 52 -- execute WUPA\n"\ + "\thf 14a raw -p 9320 -- anticollision\n"\ + "\thf 14a raw -psc 60 00 -- select and mifare AUTH\n"); + void* argtable[] = { + arg_param_begin, + arg_lit0("rR", "nreply", "do not read response"), + arg_lit0("cC", "crc", "calculate and append CRC"), + arg_lit0("pP", "power", "leave the signal field ON after receive"), + arg_lit0("aA", "active", "active signal field ON without select"), + arg_lit0("sS", "actives", "active signal field ON with select"), + arg_int0("bB", "bits", NULL, "number of bits to send. Useful for send partial byte"), + arg_int0("t", "timeout", NULL, "timeout in ms"), + arg_lit0("T", "topaz", "use Topaz protocol to send command"), + arg_lit0("3", NULL, "ISO14443-3 select only (skip RATS)"), + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + // defaults + arg_get_int(6) = 0; + arg_get_int(7) = 0; + + if (CLIParserParseString(cmd, argtable, arg_getsize(argtable), false)){ + CLIParserFree(); return 0; } - - - // strip - while (*cmd==' ' || *cmd=='\t') cmd++; - - while (cmd[i]!='\0') { - if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } - if (cmd[i]=='-') { - switch (cmd[i+1]) { - case 'r': - reply = false; - break; - case 'c': - crc = true; - break; - case 'p': - power = true; - break; - case 'a': - active = true; - break; - case 's': - active_select = true; - break; - case 'b': - sscanf(cmd+i+2,"%d",&temp); - numbits = temp & 0xFFFF; - i+=3; - while(cmd[i]!=' ' && cmd[i]!='\0') { i++; } - i-=2; - break; - case 't': - bTimeout = true; - sscanf(cmd+i+2,"%d",&temp); - timeout = temp; - i+=3; - while(cmd[i]!=' ' && cmd[i]!='\0') { i++; } - i-=2; - break; - case 'T': - topazmode = true; - break; - default: - PrintAndLog("Invalid option"); - return 0; - } - i+=2; - continue; - } - if ((cmd[i]>='0' && cmd[i]<='9') || - (cmd[i]>='a' && cmd[i]<='f') || - (cmd[i]>='A' && cmd[i]<='F') ) { - buf[strlen(buf)+1]=0; - buf[strlen(buf)]=cmd[i]; - i++; - - if (strlen(buf)>=2) { - sscanf(buf,"%x",&temp); - data[datalen]=(uint8_t)(temp & 0xff); - *buf=0; - if (datalen > sizeof(data)-1) { - if (crc) - PrintAndLog("Buffer is full, we can't add CRC to your data"); - break; - } else { - datalen++; - } - } - continue; - } - PrintAndLog("Invalid char on input"); - return 0; + + reply = !arg_get_lit(1); + crc = arg_get_lit(2); + power = arg_get_lit(3); + active = arg_get_lit(4); + active_select = arg_get_lit(5); + numbits = arg_get_int(6) & 0xFFFF; + timeout = arg_get_int(7); + bTimeout = (timeout > 0); + topazmode = arg_get_lit(8); + no_rats = arg_get_lit(9); + // len = data + CRC(2b) + if (CLIParamHexToBuf(arg_get_str(10), data, sizeof(data) -2, &datalen)) { + CLIParserFree(); + return 1; } - + + CLIParserFree(); + + // logic if(crc && datalen>0 && datalen0) + int res = 0; + if (active_select) + res = waitCmd(1); + if (!res && datalen > 0) waitCmd(0); } // if reply return 0; } -static void waitCmd(uint8_t iSelect) -{ +static int waitCmd(uint8_t iSelect) { uint8_t *recv; UsbCommand resp; char *hexout; if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { recv = resp.d.asBytes; - uint8_t iLen = iSelect ? resp.arg[1] : resp.arg[0]; - PrintAndLog("received %i octets", iLen); + uint8_t iLen = resp.arg[0]; + if (iSelect){ + iLen = resp.arg[1]; + if (iLen){ + PrintAndLog("Card selected. UID[%i]:", iLen); + } else { + PrintAndLog("Can't select card."); + } + } else { + PrintAndLog("received %i bytes:", iLen); + } if(!iLen) - return; + return 1; hexout = (char *)malloc(iLen * 3 + 1); if (hexout != NULL) { for (int i = 0; i < iLen; i++) { // data in hex @@ -755,31 +1067,33 @@ static void waitCmd(uint8_t iSelect) free(hexout); } else { PrintAndLog("malloc failed your client has low memory?"); + return 2; } } else { PrintAndLog("timeout while waiting for reply."); + return 3; } + return 0; } static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"list", CmdHF14AList, 0, "[Deprecated] List ISO 14443a history"}, - {"reader", CmdHF14AReader, 0, "Act like an ISO14443 Type A reader"}, + {"reader", CmdHF14AReader, 0, "Start acting like an ISO14443 Type A reader"}, + {"info", CmdHF14AInfo, 0, "Reads card and shows information about it"}, {"cuids", CmdHF14ACUIDs, 0, " Collect n>0 ISO14443 Type A UIDs in one go"}, {"sim", CmdHF14ASim, 0, " -- Simulate ISO 14443a tag"}, {"snoop", CmdHF14ASnoop, 0, "Eavesdrop ISO 14443 Type A"}, + {"apdu", CmdHF14AAPDU, 0, "Send an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol"}, {"raw", CmdHF14ACmdRaw, 0, "Send raw hex data to tag"}, {NULL, NULL, 0, NULL} }; int CmdHF14A(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - // parse - CmdsParse(CommandTable, Cmd); - return 0; + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; } int CmdHelp(const char *Cmd) diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index dfdf1f4a..d961d570 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -13,13 +13,22 @@ #define CMDHF14A_H__ #include +#include +#include "mifare.h" int CmdHF14A(const char *Cmd); int CmdHF14AList(const char *Cmd); int CmdHF14AMifare(const char *Cmd); int CmdHF14AReader(const char *Cmd); +extern int CmdHF14AInfo(const char *Cmd); int CmdHF14ASim(const char *Cmd); int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); +extern void DropField(); + +extern int Hf14443_4aGetCardData(iso14a_card_select_t * card); +extern int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); +extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); + #endif diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index 36932cbd..ff0bf7c9 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -8,19 +8,19 @@ // High frequency ISO14443B commands //----------------------------------------------------------------------------- +#include "cmdhf14b.h" + #include #include #include #include #include #include "iso14443crc.h" -#include "proxmark3.h" -#include "data.h" +#include "comms.h" #include "graph.h" #include "util.h" #include "ui.h" #include "cmdparser.h" -#include "cmdhf14b.h" #include "cmdmain.h" #include "cmdhf14a.h" diff --git a/client/cmdhf14b.h b/client/cmdhf14b.h index a45b7434..4fcae927 100644 --- a/client/cmdhf14b.h +++ b/client/cmdhf14b.h @@ -11,6 +11,8 @@ #ifndef CMDHF14B_H__ #define CMDHF14B_H__ +#include + int CmdHF14B(const char *Cmd); int CmdHF14BList(const char *Cmd); int CmdHF14BInfo(const char *Cmd); diff --git a/client/cmdhf15.c b/client/cmdhf15.c index 8ddbea89..e5f4af31 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -22,19 +22,20 @@ // the client. Signal Processing & decoding is done on the pc. This is the slowest // variant, but offers the possibility to analyze the waveforms directly. +#include "cmdhf15.h" + #include #include #include #include -#include "proxmark3.h" -#include "data.h" +#include "comms.h" #include "graph.h" #include "ui.h" #include "util.h" #include "cmdparser.h" -#include "cmdhf15.h" #include "iso15693tools.h" +#include "protocols.h" #include "cmdmain.h" #define FrameSOF Iso15693FrameSOF @@ -212,18 +213,17 @@ int getUID(uint8_t *buf) for (int retry=0;retry<3; retry++) { // don't give up the at the first try - req[0]= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - req[1]=ISO15_CMD_INVENTORY; - req[2]=0; // mask length - reqlen=AddCrc(req,3); - c.arg[0]=reqlen; + req[0] = ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1; + req[1] = ISO15693_INVENTORY; + req[2] = 0; // mask length + reqlen = AddCrc(req,3); + c.arg[0] = reqlen; SendCommand(&c); if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { recv = resp.d.asBytes; - if (resp.arg[0]>=12 && ISO15_CRC_CHECK==Crc(recv,12)) { + if (resp.arg[0]>=12 && ISO15693_CRC_CHECK==Crc(recv,12)) { memcpy(buf,&recv[2],8); return 1; } @@ -268,7 +268,7 @@ static char* TagErrorStr(uint8_t error) { case 0x02: return "The command is not recognised"; case 0x03: return "The option is not supported."; case 0x0f: return "Unknown error."; - case 0x10: return "The specified block is not available (doesn’t exist)."; + case 0x10: return "The specified block is not available (doesn't exist)."; case 0x11: return "The specified block is already -locked and thus cannot be locked again"; case 0x12: return "The specified block is locked and its content cannot be changed."; case 0x13: return "The specified block was not successfully programmed."; @@ -286,12 +286,12 @@ int CmdHF15Demod(const char *Cmd) int i, j; int max = 0, maxPos = 0; - int skip = 4; + int skip = 2; - if (GraphTraceLen < 1000) return 0; + if (GraphTraceLen < 2000) return 0; // First, correlate for SOF - for (i = 0; i < 100; i++) { + for (i = 0; i < 200; i++) { int corr = 0; for (j = 0; j < arraylen(FrameSOF); j += skip) { corr += FrameSOF[j] * GraphBuffer[i + (j / skip)]; @@ -310,23 +310,30 @@ int CmdHF15Demod(const char *Cmd) memset(outBuf, 0, sizeof(outBuf)); uint8_t mask = 0x01; for (;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; - for (j = 0; j < arraylen(Logic0); j += skip) { - corr0 += Logic0[j] * GraphBuffer[i + (j / skip)]; - } - for (j = 0; j < arraylen(Logic1); j += skip) { - corr1 += Logic1[j] * GraphBuffer[i + (j / skip)]; - } - for (j = 0; j < arraylen(FrameEOF); j += skip) { - corrEOF += FrameEOF[j] * GraphBuffer[i + (j / skip)]; - } - // Even things out by the length of the target waveform. - corr0 *= 4; - corr1 *= 4; - - if (corrEOF > corr1 && corrEOF > corr0) { - PrintAndLog("EOF at %d", i); - break; + int corr0 = 0, corr00 = 0, corr01 = 0, corr1 = 0, corrEOF = 0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr0 += Logic0[j]*GraphBuffer[i+(j/skip)]; + } + corr01 = corr00 = corr0; + for(j = 0; j < arraylen(Logic0); j += skip) { + corr00 += Logic0[j]*GraphBuffer[i+arraylen(Logic0)/skip+(j/skip)]; + corr01 += Logic1[j]*GraphBuffer[i+arraylen(Logic0)/skip+(j/skip)]; + } + for(j = 0; j < arraylen(Logic1); j += skip) { + corr1 += Logic1[j]*GraphBuffer[i+(j/skip)]; + } + for(j = 0; j < arraylen(FrameEOF); j += skip) { + corrEOF += FrameEOF[j]*GraphBuffer[i+(j/skip)]; + } + // Even things out by the length of the target waveform. + corr00 *= 2; + corr01 *= 2; + corr0 *= 4; + corr1 *= 4; + + if(corrEOF > corr1 && corrEOF > corr00 && corrEOF > corr01) { + PrintAndLog("EOF at %d", i); + break; } else if (corr1 > corr0) { i += arraylen(Logic1) / skip; outBuf[k] |= mask; @@ -366,7 +373,8 @@ int CmdHF15Read(const char *Cmd) return 0; } -// Record Activity without enabeling carrier +// Record Activity without enabling carrier +// TODO: currently it DOES enable the carrier int CmdHF15Record(const char *Cmd) { UsbCommand c = {CMD_RECORD_RAW_ADC_SAMPLES_ISO_15693}; @@ -416,6 +424,7 @@ int CmdHF15Sim(const char *Cmd) PrintAndLog("Starting simulating UID %02X %02X %02X %02X %02X %02X %02X %02X", uid[0],uid[1],uid[2],uid[3],uid[4], uid[5], uid[6], uid[7]); + PrintAndLog("Press the button to stop simulation"); UsbCommand c = {CMD_SIMTAG_ISO_15693, {0, 0, 0}}; memcpy(c.d.asBytes,uid,8); @@ -454,20 +463,19 @@ int CmdHF15DumpMem(const char*Cmd) { for (int retry=0; retry<5; retry++) { - req[0]= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[1]=ISO15_CMD_READ; + req[0]= ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS; + req[1] = ISO15693_READBLOCK; memcpy(&req[2],uid,8); - req[10]=blocknum; - reqlen=AddCrc(req,11); - c.arg[0]=reqlen; + req[10] = blocknum; + reqlen = AddCrc(req,11); + c.arg[0] = reqlen; SendCommand(&c); if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { retry=0; *output=0; // reset outputstring sprintf(output, "Block %02x ",blocknum); @@ -491,7 +499,7 @@ int CmdHF15DumpMem(const char*Cmd) { // TODO: need fix // if (resp.arg[0]<3) // PrintAndLog("Lost Connection"); -// else if (ISO15_CRC_CHECK!=Crc(resp.d.asBytes,resp.arg[0])) +// else if (ISO15693_CRC_CHECK!=Crc(resp.d.asBytes,resp.arg[0])) // PrintAndLog("CRC Failed"); // else // PrintAndLog("Tag returned Error %i: %s",recv[1],TagErrorStr(recv[1])); @@ -512,7 +520,7 @@ static command_t CommandTable15[] = {"cmd", CmdHF15Cmd, 0, "Send direct commands to ISO15693 tag"}, {"findafi", CmdHF15Afi, 0, "Brute force AFI of an ISO15693 tag"}, {"dumpmemory", CmdHF15DumpMem, 0, "Read all memory pages of an ISO15693 tag"}, - {NULL, NULL, 0, NULL} + {NULL, NULL, 0, NULL} }; int CmdHF15(const char *Cmd) @@ -539,12 +547,11 @@ int CmdHF15CmdInquiry(const char *Cmd) uint8_t *req=c.d.asBytes; int reqlen=0; - req[0]= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - req[1]=ISO15_CMD_INVENTORY; - req[2]=0; // mask length + req[0] = ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1; + req[1] = ISO15693_INVENTORY; + req[2] = 0; // mask length reqlen=AddCrc(req,3); - c.arg[0]=reqlen; + c.arg[0] = reqlen; SendCommand(&c); @@ -565,9 +572,9 @@ int CmdHF15CmdInquiry(const char *Cmd) // Turns debugging on(1)/off(0) int CmdHF15CmdDebug( const char *cmd) { - int debug=atoi(cmd); - if (strlen(cmd)<1) { - PrintAndLog("Usage: hf 15 cmd debug <0|1>"); + int debug = atoi(cmd); + if (strlen(cmd) < 1) { + PrintAndLog("Usage: hf 15 debug <0|1>"); PrintAndLog(" 0 no debugging"); PrintAndLog(" 1 turn debugging on"); return 0; @@ -698,7 +705,7 @@ int prepareHF15Cmd(char **cmd, UsbCommand *c, uint8_t iso15cmd[], int iso15cmdle while (**cmd==' ' || **cmd=='\t') (*cmd)++; if (strstr(*cmd,"-o")==*cmd) { - req[reqlen]=ISO15_REQ_OPTION; + req[reqlen]=ISO15693_REQ_OPTION; (*cmd)+=2; } @@ -713,36 +720,32 @@ int prepareHF15Cmd(char **cmd, UsbCommand *c, uint8_t iso15cmd[], int iso15cmdle case 's': case 'S': // you must have selected the tag earlier - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_SELECT; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); - reqlen+=iso15cmdlen; + req[reqlen++] |= ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_SELECT; + memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); + reqlen += iso15cmdlen; break; case 'u': case 'U': // unaddressed mode may not be supported by all vendors - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); - reqlen+=iso15cmdlen; + req[reqlen++] |= ISO15693_REQ_DATARATE_HIGH; + memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); + reqlen += iso15cmdlen; break; case '*': // we scan for the UID ourself - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); + req[reqlen++] |= ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS; + memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); reqlen+=iso15cmdlen; - if (!getUID(uid)) { - PrintAndLog("No Tag found"); - return 0; - } - memcpy(req+reqlen,uid,8); - PrintAndLog("Detected UID %s",sprintUID(NULL,uid)); - reqlen+=8; + if (!getUID(uid)) { + PrintAndLog("No Tag found"); + return 0; + } + memcpy(req+reqlen,uid,8); + PrintAndLog("Detected UID %s",sprintUID(NULL,uid)); + reqlen+=8; break; default: - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; + req[reqlen++] |= ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS; memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); reqlen+=iso15cmdlen; @@ -801,7 +804,7 @@ int CmdHF15CmdSysinfo(const char *Cmd) { return 0; } - prepareHF15Cmd(&cmd, &c,(uint8_t[]){ISO15_CMD_SYSINFO},1); + prepareHF15Cmd(&cmd, &c,(uint8_t[]){ISO15693_GET_SYSTEM_INFO},1); reqlen=c.arg[0]; reqlen=AddCrc(req,reqlen); @@ -811,8 +814,8 @@ int CmdHF15CmdSysinfo(const char *Cmd) { if (WaitForResponseTimeout(CMD_ACK,&resp,1000) && resp.arg[0]>2) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { *output=0; // reset outputstring for ( i=1; i2) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { *output=0; // reset outputstring for ( int i=1; i2) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { *output=0; // reset outputstring //sprintf(output, "Block %2i ",blocknum); for ( int i=1; i page num ; *cmd2 -> data @@ -1078,8 +1081,8 @@ int CmdHF15CmdWrite(const char *Cmd) { if (WaitForResponseTimeout(CMD_ACK,&resp,2000) && resp.arg[0]>2) { recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { + if (ISO15693_CRC_CHECK==Crc(recv,resp.arg[0])) { + if (!(recv[0] & ISO15693_RES_ERROR)) { PrintAndLog("OK"); } else { PrintAndLog("Tag returned Error %i: %s",recv[1],TagErrorStr(recv[1])); diff --git a/client/cmdhf15.h b/client/cmdhf15.h index c6264604..d0517fe5 100644 --- a/client/cmdhf15.h +++ b/client/cmdhf15.h @@ -11,6 +11,8 @@ #ifndef CMDHF15_H__ #define CMDHF15_H__ +#include + int CmdHF15(const char *Cmd); int CmdHF15Demod(const char *Cmd); diff --git a/client/cmdhfepa.c b/client/cmdhfepa.c index f3456afb..76664bf5 100644 --- a/client/cmdhfepa.c +++ b/client/cmdhfepa.c @@ -17,7 +17,7 @@ #include #include "util.h" #include "util_posix.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" #include "common.h" @@ -202,10 +202,7 @@ int CmdHelp(const char *Cmd) int CmdHFEPA(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - // parse - CmdsParse(CommandTable, Cmd); - return 0; + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; } diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index d02eff8d..499f7aae 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -14,9 +14,9 @@ #include #include #include +#include #include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type -#include "data.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" #include "cmdhficlass.h" @@ -32,6 +32,7 @@ #include "protocols.h" #include "usb_cmd.h" #include "cmdhfmfu.h" +#include "util_posix.h" static int CmdHelp(const char *Cmd); @@ -51,6 +52,20 @@ typedef struct iclass_block { uint8_t d[8]; } iclass_block_t; +int usage_hf_iclass_chk(void) { + PrintAndLog("Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag"); + PrintAndLog("Usage: hf iclass chk [h|e|r] "); + PrintAndLog("Options:"); + PrintAndLog("h Show this help"); + PrintAndLog("f Dictionary file with default iclass keys"); + PrintAndLog(" e target Elite / High security key scheme"); + PrintAndLog(" r interpret dictionary file as raw (diversified keys)"); + PrintAndLog("Samples:"); + PrintAndLog(" hf iclass chk f default_iclass_keys.dic"); + PrintAndLog(" hf iclass chk f default_iclass_keys.dic e"); + return 0; +} + int xorbits_8(uint8_t val) { uint8_t res = val ^ (val >> 1); //1st pass res = res ^ (res >> 1); // 2nd pass @@ -278,7 +293,7 @@ int CmdHFiClassELoad(const char *Cmd) { //File handling and reading FILE *f; char filename[FILE_PATH_SIZE]; - if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) + if(opt == 'f' && param_getstr(Cmd, 1, filename, sizeof(filename)) > 0) { f = fopen(filename, "rb"); }else{ @@ -384,7 +399,7 @@ int CmdHFiClassDecrypt(const char *Cmd) { //Open the tagdump-file FILE *f; char filename[FILE_PATH_SIZE]; - if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) { + if(opt == 'f' && param_getstr(Cmd, 1, filename, sizeof(filename)) > 0) { f = fopen(filename, "rb"); if ( f == NULL ) { PrintAndLog("Could not find file %s", filename); @@ -531,7 +546,7 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u memcpy(div_key, KEY, 8); else HFiClassCalcDivKey(CSN, KEY, div_key, elite); - PrintAndLog("Authing with %s: %02x%02x%02x%02x%02x%02x%02x%02x", rawkey ? "raw key" : "diversified key", div_key[0],div_key[1],div_key[2],div_key[3],div_key[4],div_key[5],div_key[6],div_key[7]); + if (verbose) PrintAndLog("Authing with %s: %02x%02x%02x%02x%02x%02x%02x%02x", rawkey ? "raw key" : "diversified key", div_key[0],div_key[1],div_key[2],div_key[3],div_key[4],div_key[5],div_key[6],div_key[7]); doMAC(CCNR, div_key, MAC); UsbCommand resp; @@ -541,12 +556,12 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u SendCommand(&d); if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) { - PrintAndLog("Auth Command execute timeout"); + if (verbose) PrintAndLog("Auth Command execute timeout"); return false; } uint8_t isOK = resp.arg[0] & 0xff; if (!isOK) { - PrintAndLog("Authentication error"); + if (verbose) PrintAndLog("Authentication error"); return false; } return true; @@ -605,7 +620,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { case 'c': case 'C': have_credit_key = true; - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, CreditKEY, dataLen); } else if (dataLen == 1) { @@ -629,7 +644,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { break; case 'f': case 'F': - fileNameLen = param_getstr(Cmd, cmdp+1, filename); + fileNameLen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (fileNameLen < 1) { PrintAndLog("No filename found after f"); errors = true; @@ -639,7 +654,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { case 'k': case 'K': have_debit_key = true; - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, KEY, dataLen); } else if (dataLen == 1) { @@ -683,7 +698,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { SendCommand(&c); if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { PrintAndLog("Command execute timeout"); - ul_switch_off_field(); + DropField(); return 0; } uint8_t readStatus = resp.arg[0] & 0xff; @@ -691,7 +706,7 @@ int CmdHFiClassReader_Dump(const char *Cmd) { if(readStatus == 0){ PrintAndLog("No tag found..."); - ul_switch_off_field(); + DropField(); return 0; } if( readStatus & (FLAG_ICLASS_READER_CSN|FLAG_ICLASS_READER_CONF|FLAG_ICLASS_READER_CC)){ @@ -702,12 +717,12 @@ int CmdHFiClassReader_Dump(const char *Cmd) { // large memory - not able to dump pages currently if (numblks > maxBlk) numblks = maxBlk; } - ul_switch_off_field(); + DropField(); // authenticate debit key and get div_key - later store in dump block 3 if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, false)){ //try twice - for some reason it sometimes fails the first time... if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, false)){ - ul_switch_off_field(); + DropField(); return 0; } } @@ -718,14 +733,14 @@ int CmdHFiClassReader_Dump(const char *Cmd) { SendCommand(&w); if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { PrintAndLog("Command execute time-out 1"); - ul_switch_off_field(); + DropField(); return 1; } uint32_t blocksRead = resp.arg[1]; uint8_t isOK = resp.arg[0] & 0xff; if (!isOK && !blocksRead) { PrintAndLog("Read Block Failed"); - ul_switch_off_field(); + DropField(); return 0; } uint32_t startindex = resp.arg[2]; @@ -734,20 +749,19 @@ int CmdHFiClassReader_Dump(const char *Cmd) { blocksRead = (sizeof(tag_data)/8) - blockno; } // response ok - now get bigbuf content of the dump - GetFromBigBuf(tag_data+(blockno*8), blocksRead*8, startindex); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(tag_data+(blockno*8), blocksRead*8, startindex, NULL, -1, false); size_t gotBytes = blocksRead*8 + blockno*8; // try AA2 if (have_credit_key) { //turn off hf field before authenticating with different key - ul_switch_off_field(); + DropField(); memset(MAC,0,4); // AA2 authenticate credit key and git c_div_key - later store in dump block 4 if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false, false)){ //try twice - for some reason it sometimes fails the first time... if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false, false)){ - ul_switch_off_field(); + DropField(); return 0; } } @@ -760,14 +774,14 @@ int CmdHFiClassReader_Dump(const char *Cmd) { SendCommand(&w); if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { PrintAndLog("Command execute timeout 2"); - ul_switch_off_field(); + DropField(); return 0; } uint8_t isOK = resp.arg[0] & 0xff; blocksRead = resp.arg[1]; if (!isOK && !blocksRead) { PrintAndLog("Read Block Failed 2"); - ul_switch_off_field(); + DropField(); return 0; } @@ -777,12 +791,11 @@ int CmdHFiClassReader_Dump(const char *Cmd) { blocksRead = (sizeof(tag_data) - gotBytes)/8; } // get dumped data from bigbuf - GetFromBigBuf(tag_data+gotBytes, blocksRead*8, startindex); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(tag_data+gotBytes, blocksRead*8, startindex, NULL, -1, false); gotBytes += blocksRead*8; } else { //field is still on - turn it off... - ul_switch_off_field(); + DropField(); } } @@ -898,7 +911,7 @@ int CmdHFiClass_WriteBlock(const char *Cmd) { break; case 'k': case 'K': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, KEY, dataLen); } else if (dataLen == 1) { @@ -930,7 +943,7 @@ int CmdHFiClass_WriteBlock(const char *Cmd) { if (cmdp < 6) return usage_hf_iclass_writeblock(); int ans = WriteBlock(blockno, bldata, KEY, use_credit_key, elite, rawkey, true); - ul_switch_off_field(); + DropField(); return ans; } @@ -992,7 +1005,7 @@ int CmdHFiClassCloneTag(const char *Cmd) { break; case 'f': case 'F': - fileNameLen = param_getstr(Cmd, cmdp+1, filename); + fileNameLen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (fileNameLen < 1) { PrintAndLog("No filename found after f"); errors = true; @@ -1001,7 +1014,7 @@ int CmdHFiClassCloneTag(const char *Cmd) { break; case 'k': case 'K': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, KEY, dataLen); } else if (dataLen == 1) { @@ -1196,7 +1209,7 @@ int CmdHFiClass_ReadBlock(const char *Cmd) { case 'k': case 'K': auth = true; - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, KEY, dataLen); } else if (dataLen == 1) { @@ -1253,7 +1266,7 @@ int CmdHFiClass_loclass(const char *Cmd) { char fileName[255] = {0}; if(opt == 'f') { - if(param_getstr(Cmd, 1, fileName) > 0) + if(param_getstr(Cmd, 1, fileName, sizeof(fileName)) > 0) { return bruteforceFileNoKeys(fileName); }else @@ -1318,14 +1331,14 @@ int CmdHFiClassReadTagFile(const char *Cmd) { char tempnum[5]; FILE *f; char filename[FILE_PATH_SIZE]; - if (param_getstr(Cmd, 0, filename) < 1) + if (param_getstr(Cmd, 0, filename, sizeof(filename)) < 1) return usage_hf_iclass_readtagfile(); - if (param_getstr(Cmd,1,(char *)&tempnum) < 1) + if (param_getstr(Cmd, 1, tempnum, sizeof(tempnum)) < 1) startblock = 0; else sscanf(tempnum,"%d",&startblock); - if (param_getstr(Cmd,2,(char *)&tempnum) < 1) + if (param_getstr(Cmd,2, tempnum, sizeof(tempnum)) < 1) endblock = 0; else sscanf(tempnum,"%d",&endblock); @@ -1458,7 +1471,7 @@ int CmdHFiClassCalcNewKey(const char *Cmd) { return usage_hf_iclass_calc_newkey(); case 'e': case 'E': - dataLen = param_getstr(Cmd, cmdp, tempStr); + dataLen = param_getstr(Cmd, cmdp, tempStr, sizeof(tempStr)); if (dataLen==2) oldElite = true; elite = true; @@ -1466,7 +1479,7 @@ int CmdHFiClassCalcNewKey(const char *Cmd) { break; case 'n': case 'N': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, NEWKEY, dataLen); } else if (dataLen == 1) { @@ -1485,7 +1498,7 @@ int CmdHFiClassCalcNewKey(const char *Cmd) { break; case 'o': case 'O': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { errors = param_gethex(tempStr, 0, OLDKEY, dataLen); } else if (dataLen == 1) { @@ -1626,7 +1639,7 @@ int CmdHFiClassManageKeys(const char *Cmd) { return usage_hf_iclass_managekeys(); case 'f': case 'F': - fileNameLen = param_getstr(Cmd, cmdp+1, filename); + fileNameLen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (fileNameLen < 1) { PrintAndLog("No filename found after f"); errors = true; @@ -1645,7 +1658,7 @@ int CmdHFiClassManageKeys(const char *Cmd) { case 'k': case 'K': operation += 3; //set key - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 16) { //ul-c or ev1/ntag key length errors = param_gethex(tempStr, 0, KEY, dataLen); } else { @@ -1699,10 +1712,152 @@ int CmdHFiClassManageKeys(const char *Cmd) { return 0; } +int CmdHFiClassCheckKeys(const char *Cmd) { + + uint8_t mac[4] = {0x00,0x00,0x00,0x00}; + 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}; + + // elite key, raw key, standard key + bool use_elite = false; + bool use_raw = false; + bool found_debit = false; + bool found_credit = false; + bool errors = false; + uint8_t cmdp = 0x00; + FILE * f; + char filename[FILE_PATH_SIZE] = {0}; + uint8_t fileNameLen = 0; + char buf[17]; + uint8_t *keyBlock = NULL, *p; + int keyitems = 0, keycnt = 0; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (param_getchar(Cmd, cmdp)) { + case 'h': + case 'H': + return usage_hf_iclass_chk(); + case 'f': + case 'F': + fileNameLen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); + if (fileNameLen < 1) { + PrintAndLog("No filename found after f"); + errors = true; + } + cmdp += 2; + break; + case 'e': + case 'E': + use_elite = true; + cmdp++; + break; + case 'r': + case 'R': + use_raw = true; + cmdp++; + break; + default: + PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + if (errors) return usage_hf_iclass_chk(); + + if ( !(f = fopen( filename , "r")) ) { + PrintAndLog("File: %s: not found or locked.", filename); + return 1; + } + + while( fgets(buf, sizeof(buf), f) ){ + if (strlen(buf) < 16 || buf[15] == '\n') + continue; + + while (fgetc(f) != '\n' && !feof(f)) ; //goto next line + + if( buf[0]=='#' ) continue; //The line start with # is comment, skip + + if (!isxdigit(buf[0])){ + PrintAndLog("File content error. '%s' must include 16 HEX symbols",buf); + continue; + } + + buf[16] = 0; + + p = realloc(keyBlock, 8 * (keyitems += 64)); + if (!p) { + PrintAndLog("Cannot allocate memory for default keys"); + free(keyBlock); + fclose(f); + return 2; + } + keyBlock = p; + + memset(keyBlock + 8 * keycnt, 0, 8); + num_to_bytes(strtoull(buf, NULL, 16), 8, keyBlock + 8 * keycnt); + + //PrintAndLog("check key[%2d] %016" PRIx64, keycnt, bytes_to_num(keyBlock + 8*keycnt, 8)); + keycnt++; + memset(buf, 0, sizeof(buf)); + } + fclose(f); + PrintAndLog("Loaded %2d keys from %s", keycnt, filename); + + // time + uint64_t t1 = msclock(); + + for (uint32_t c = 0; c < keycnt; c += 1) { + printf("."); fflush(stdout); + if (ukbhit()) { + int gc = getchar(); (void)gc; + printf("\naborted via keyboard!\n"); + break; + } + + memcpy(key, keyBlock + 8 * c , 8); + + // debit key. try twice + for (int foo = 0; foo < 2 && !found_debit; foo++) { + if (!select_and_auth(key, mac, div_key, false, use_elite, use_raw, false)) + continue; + + // key found. + PrintAndLog("\n--------------------------------------------------------"); + PrintAndLog(" Found AA1 debit key\t\t[%s]", sprint_hex(key, 8)); + found_debit = true; + } + + // credit key. try twice + for (int foo = 0; foo < 2 && !found_credit; foo++) { + if (!select_and_auth(key, mac, div_key, true, use_elite, use_raw, false)) + continue; + + // key found + PrintAndLog("\n--------------------------------------------------------"); + PrintAndLog(" Found AA2 credit key\t\t[%s]", sprint_hex(key, 8)); + found_credit = true; + } + + // both keys found. + if ( found_debit && found_credit ) + break; + } + + t1 = msclock() - t1; + + PrintAndLog("\nTime in iclass checkkeys: %.0f seconds\n", (float)t1/1000.0); + + DropField(); + free(keyBlock); + PrintAndLog(""); + return 0; +} + static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"calcnewkey", CmdHFiClassCalcNewKey, 1, "[options..] Calc Diversified keys (blocks 3 & 4) to write new keys"}, + {"chk", CmdHFiClassCheckKeys, 0, " Check keys"}, {"clone", CmdHFiClassCloneTag, 0, "[options..] Authenticate and Clone from iClass bin file"}, {"decrypt", CmdHFiClassDecrypt, 1, "[f ] Decrypt tagdump" }, {"dump", CmdHFiClassReader_Dump, 0, "[options..] Authenticate and Dump iClass tag's AA1"}, @@ -1723,6 +1878,7 @@ static command_t CommandTable[] = int CmdHFiClass(const char *Cmd) { + clearCommandBuffer(); CmdsParse(CommandTable, Cmd); return 0; } diff --git a/client/cmdhficlass.h b/client/cmdhficlass.h index 9a3adf50..456bb1c3 100644 --- a/client/cmdhficlass.h +++ b/client/cmdhficlass.h @@ -34,6 +34,7 @@ int CmdHFiClassSnoop(const char *Cmd); int CmdHFiClassSim(const char *Cmd); int CmdHFiClassWriteKeyFile(const char *Cmd); int CmdHFiClass_WriteBlock(const char *Cmd); +int CmdHFiClassCheckKeys(const char *Cmd); void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize); void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite); #endif diff --git a/client/cmdhflegic.c b/client/cmdhflegic.c index c9f3485e..8fbd4578 100644 --- a/client/cmdhflegic.c +++ b/client/cmdhflegic.c @@ -8,16 +8,17 @@ // High frequency Legic commands //----------------------------------------------------------------------------- +#include "cmdhflegic.h" + #include #include #include -#include "proxmark3.h" -#include "data.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" -#include "cmdhflegic.h" #include "cmdmain.h" #include "util.h" + static int CmdHelp(const char *Cmd); static command_t CommandTable[] = @@ -27,7 +28,7 @@ static command_t CommandTable[] = {"reader", CmdLegicRFRead, 0, "[offset [length]] -- read bytes from a LEGIC card"}, {"save", CmdLegicSave, 0, " [] -- Store samples"}, {"load", CmdLegicLoad, 0, " -- Restore samples"}, - {"sim", CmdLegicRfSim, 0, "[phase drift [frame drift [req/resp drift]]] Start tag simulator (use after load or read)"}, + {"sim", CmdLegicRfSim, 0, "[tagtype, 0:MIM22, 1:MIM256, 2:MIM1024] Start tag simulator (use after load or read)"}, {"write", CmdLegicRfWrite,0, " -- Write sample buffer (user after load or read)"}, {"fill", CmdLegicRfFill, 0, " -- Fill/Write tag with constant value"}, {NULL, NULL, 0, NULL} @@ -64,8 +65,7 @@ int CmdLegicDecode(const char *Cmd) char token_type[4]; // copy data from proxmark into buffer - GetFromBigBuf(data_buf,sizeof(data_buf),0); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(data_buf, sizeof(data_buf), 0, NULL, -1, false); // Output CDF System area (9 bytes) plus remaining header area (12 bytes) @@ -294,8 +294,7 @@ int CmdLegicSave(const char *Cmd) return -1; } - GetFromBigBuf(got,requested,offset); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, requested, offset, NULL, -1, false); for (int j = 0; j < requested; j += 8) { fprintf(f, "%02x %02x %02x %02x %02x %02x %02x %02x\n", @@ -321,10 +320,8 @@ int CmdLegicSave(const char *Cmd) int CmdLegicRfSim(const char *Cmd) { UsbCommand c={CMD_SIMULATE_TAG_LEGIC_RF}; - c.arg[0] = 6; - c.arg[1] = 3; - c.arg[2] = 0; - sscanf(Cmd, " %" SCNi64 " %" SCNi64 " %" SCNi64, &c.arg[0], &c.arg[1], &c.arg[2]); + c.arg[0] = 1; + sscanf(Cmd, " %" SCNi64, &c.arg[0]); SendCommand(&c); return 0; } diff --git a/client/cmdhflist.c b/client/cmdhflist.c new file mode 100644 index 00000000..5538b6fb --- /dev/null +++ b/client/cmdhflist.c @@ -0,0 +1,606 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Merlok - 2017 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Command: hf mf list. It shows data from arm buffer. +//----------------------------------------------------------------------------- + +#include "cmdhflist.h" + +#include +#include +#include +#include +#include +#include "util.h" +#include "ui.h" +#include "iso14443crc.h" +#include "parity.h" +#include "protocols.h" +#include "crapto1/crapto1.h" +#include "mifarehost.h" +#include "mifaredefault.h" + + +enum MifareAuthSeq { + masNone, + masNt, + masNrAr, + masAt, + masAuthComplete, + masFirstData, + masData, + masError, +}; +static enum MifareAuthSeq MifareAuthState; +static TAuthData AuthData; + +void ClearAuthData() { + AuthData.uid = 0; + AuthData.nt = 0; + AuthData.first_auth = true; + AuthData.ks2 = 0; + AuthData.ks3 = 0; +} + +/** + * @brief iso14443A_CRC_check Checks CRC in command or response + * @param isResponse + * @param data + * @param len + * @return 0 : CRC-command, CRC not ok + * 1 : CRC-command, CRC ok + * 2 : Not crc-command + */ +uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len) +{ + uint8_t b1,b2; + + if(len <= 2) return 2; + + if(isResponse & (len < 6)) return 2; + + ComputeCrc14443(CRC_14443_A, data, len-2, &b1, &b2); + if (b1 != data[len-2] || b2 != data[len-1]) { + return 0; + } else { + return 1; + } +} + +uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len) +{ + switch(MifareAuthState) { + case masNone: + case masError: + return iso14443A_CRC_check(isResponse, data, len); + default: + return 2; + } +} + +void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + switch(cmd[0]) + { + case ICLASS_CMD_ACTALL: snprintf(exp,size,"ACTALL"); break; + case ICLASS_CMD_READ_OR_IDENTIFY:{ + if(cmdsize > 1){ + snprintf(exp,size,"READ(%d)",cmd[1]); + }else{ + snprintf(exp,size,"IDENTIFY"); + } + break; + } + case ICLASS_CMD_SELECT: snprintf(exp,size,"SELECT"); break; + case ICLASS_CMD_PAGESEL: snprintf(exp,size,"PAGESEL(%d)", cmd[1]); break; + case ICLASS_CMD_READCHECK_KC:snprintf(exp,size,"READCHECK[Kc](%d)", cmd[1]); break; + case ICLASS_CMD_READCHECK_KD:snprintf(exp,size,"READCHECK[Kd](%d)", cmd[1]); break; + case ICLASS_CMD_CHECK: snprintf(exp,size,"CHECK"); break; + case ICLASS_CMD_DETECT: snprintf(exp,size,"DETECT"); break; + case ICLASS_CMD_HALT: snprintf(exp,size,"HALT"); break; + case ICLASS_CMD_UPDATE: snprintf(exp,size,"UPDATE(%d)",cmd[1]); break; + case ICLASS_CMD_ACT: snprintf(exp,size,"ACT"); break; + case ICLASS_CMD_READ4: snprintf(exp,size,"READ4(%d)",cmd[1]); break; + default: snprintf(exp,size,"?"); break; + } + return; +} + +void annotateIso15693(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + + if(cmd[0] == 0x26) + { + switch(cmd[1]){ + case ISO15693_INVENTORY :snprintf(exp, size, "INVENTORY");break; + case ISO15693_STAYQUIET :snprintf(exp, size, "STAY_QUIET");break; + default: snprintf(exp,size,"?"); break; + + } + }else if(cmd[0] == 0x02) + { + switch(cmd[1]) + { + case ISO15693_READBLOCK :snprintf(exp, size, "READBLOCK");break; + case ISO15693_WRITEBLOCK :snprintf(exp, size, "WRITEBLOCK");break; + case ISO15693_LOCKBLOCK :snprintf(exp, size, "LOCKBLOCK");break; + case ISO15693_READ_MULTI_BLOCK :snprintf(exp, size, "READ_MULTI_BLOCK");break; + case ISO15693_SELECT :snprintf(exp, size, "SELECT");break; + case ISO15693_RESET_TO_READY :snprintf(exp, size, "RESET_TO_READY");break; + case ISO15693_WRITE_AFI :snprintf(exp, size, "WRITE_AFI");break; + case ISO15693_LOCK_AFI :snprintf(exp, size, "LOCK_AFI");break; + case ISO15693_WRITE_DSFID :snprintf(exp, size, "WRITE_DSFID");break; + case ISO15693_LOCK_DSFID :snprintf(exp, size, "LOCK_DSFID");break; + case ISO15693_GET_SYSTEM_INFO :snprintf(exp, size, "GET_SYSTEM_INFO");break; + case ISO15693_READ_MULTI_SECSTATUS :snprintf(exp, size, "READ_MULTI_SECSTATUS");break; + default: snprintf(exp,size,"?"); break; + } + } +} + + +void annotateTopaz(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + switch(cmd[0]) { + case TOPAZ_REQA :snprintf(exp, size, "REQA");break; + case TOPAZ_WUPA :snprintf(exp, size, "WUPA");break; + case TOPAZ_RID :snprintf(exp, size, "RID");break; + case TOPAZ_RALL :snprintf(exp, size, "RALL");break; + case TOPAZ_READ :snprintf(exp, size, "READ");break; + case TOPAZ_WRITE_E :snprintf(exp, size, "WRITE-E");break; + case TOPAZ_WRITE_NE :snprintf(exp, size, "WRITE-NE");break; + case TOPAZ_RSEG :snprintf(exp, size, "RSEG");break; + case TOPAZ_READ8 :snprintf(exp, size, "READ8");break; + case TOPAZ_WRITE_E8 :snprintf(exp, size, "WRITE-E8");break; + case TOPAZ_WRITE_NE8 :snprintf(exp, size, "WRITE-NE8");break; + default: snprintf(exp,size,"?"); break; + } +} + + +/** +06 00 = INITIATE +0E xx = SELECT ID (xx = Chip-ID) +0B = Get UID +08 yy = Read Block (yy = block number) +09 yy dd dd dd dd = Write Block (yy = block number; dd dd dd dd = data to be written) +0C = Reset to Inventory +0F = Completion +0A 11 22 33 44 55 66 = Authenticate (11 22 33 44 55 66 = data to authenticate) +**/ + +void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + switch(cmd[0]){ + case ISO14443B_REQB : snprintf(exp,size,"REQB");break; + case ISO14443B_ATTRIB : snprintf(exp,size,"ATTRIB");break; + case ISO14443B_HALT : snprintf(exp,size,"HALT");break; + case ISO14443B_INITIATE : snprintf(exp,size,"INITIATE");break; + case ISO14443B_SELECT : snprintf(exp,size,"SELECT(%d)",cmd[1]);break; + case ISO14443B_GET_UID : snprintf(exp,size,"GET UID");break; + case ISO14443B_READ_BLK : snprintf(exp,size,"READ_BLK(%d)", cmd[1]);break; + case ISO14443B_WRITE_BLK : snprintf(exp,size,"WRITE_BLK(%d)",cmd[1]);break; + case ISO14443B_RESET : snprintf(exp,size,"RESET");break; + case ISO14443B_COMPLETION : snprintf(exp,size,"COMPLETION");break; + case ISO14443B_AUTHENTICATE : snprintf(exp,size,"AUTHENTICATE");break; + default : snprintf(exp,size ,"?");break; + } + +} + +void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + switch(cmd[0]) + { + case ISO14443A_CMD_WUPA: + snprintf(exp,size,"WUPA"); + break; + case ISO14443A_CMD_ANTICOLL_OR_SELECT:{ + // 93 20 = Anticollision (usage: 9320 - answer: 4bytes UID+1byte UID-bytes-xor) + // 93 70 = Select (usage: 9370+5bytes 9320 answer - answer: 1byte SAK) + if(cmd[1] == 0x70) + { + snprintf(exp,size,"SELECT_UID"); break; + }else + { + snprintf(exp,size,"ANTICOLL"); break; + } + } + case ISO14443A_CMD_ANTICOLL_OR_SELECT_2:{ + //95 20 = Anticollision of cascade level2 + //95 70 = Select of cascade level2 + if(cmd[2] == 0x70) + { + snprintf(exp,size,"SELECT_UID-2"); break; + }else + { + snprintf(exp,size,"ANTICOLL-2"); break; + } + } + case ISO14443A_CMD_REQA: + snprintf(exp,size,"REQA"); + break; + case ISO14443A_CMD_READBLOCK: snprintf(exp,size,"READBLOCK(%d)",cmd[1]); break; + case ISO14443A_CMD_WRITEBLOCK: snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); break; + case ISO14443A_CMD_HALT: + snprintf(exp,size,"HALT"); + MifareAuthState = masNone; + break; + case ISO14443A_CMD_RATS: snprintf(exp,size,"RATS"); break; + case MIFARE_CMD_INC: snprintf(exp,size,"INC(%d)",cmd[1]); break; + case MIFARE_CMD_DEC: snprintf(exp,size,"DEC(%d)",cmd[1]); break; + case MIFARE_CMD_RESTORE: snprintf(exp,size,"RESTORE(%d)",cmd[1]); break; + case MIFARE_CMD_TRANSFER: snprintf(exp,size,"TRANSFER(%d)",cmd[1]); break; + case MIFARE_AUTH_KEYA: + if ( cmdsize > 3) { + snprintf(exp,size,"AUTH-A(%d)",cmd[1]); + MifareAuthState = masNt; + } else { + // case MIFARE_ULEV1_VERSION : both 0x60. + snprintf(exp,size,"EV1 VERSION"); + } + break; + case MIFARE_AUTH_KEYB: + MifareAuthState = masNt; + snprintf(exp,size,"AUTH-B(%d)",cmd[1]); + break; + case MIFARE_MAGICWUPC1: snprintf(exp,size,"MAGIC WUPC1"); break; + case MIFARE_MAGICWUPC2: snprintf(exp,size,"MAGIC WUPC2"); break; + case MIFARE_MAGICWIPEC: snprintf(exp,size,"MAGIC WIPEC"); break; + case MIFARE_ULC_AUTH_1: snprintf(exp,size,"AUTH "); break; + case MIFARE_ULC_AUTH_2: snprintf(exp,size,"AUTH_ANSW"); break; + case MIFARE_ULEV1_AUTH: + if ( cmdsize == 7 ) + snprintf(exp,size,"PWD-AUTH KEY: 0x%02x%02x%02x%02x", cmd[1], cmd[2], cmd[3], cmd[4] ); + else + snprintf(exp,size,"PWD-AUTH"); + break; + case MIFARE_ULEV1_FASTREAD:{ + if ( cmdsize >=3 && cmd[2] <= 0xE6) + snprintf(exp,size,"READ RANGE (%d-%d)",cmd[1],cmd[2]); + else + snprintf(exp,size,"?"); + break; + } + case MIFARE_ULC_WRITE:{ + if ( cmd[1] < 0x21 ) + snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); + else + snprintf(exp,size,"?"); + break; + } + case MIFARE_ULEV1_READ_CNT:{ + if ( cmd[1] < 5 ) + snprintf(exp,size,"READ CNT(%d)",cmd[1]); + else + snprintf(exp,size,"?"); + break; + } + case MIFARE_ULEV1_INCR_CNT:{ + if ( cmd[1] < 5 ) + snprintf(exp,size,"INCR(%d)",cmd[1]); + else + snprintf(exp,size,"?"); + break; + } + case MIFARE_ULEV1_READSIG: snprintf(exp,size,"READ_SIG"); break; + case MIFARE_ULEV1_CHECKTEAR: snprintf(exp,size,"CHK_TEARING(%d)",cmd[1]); break; + case MIFARE_ULEV1_VCSL: snprintf(exp,size,"VCSL"); break; + default: snprintf(exp,size,"?"); break; + } + return; +} + +void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse) { + if (!isResponse && cmdsize == 1) { + switch(cmd[0]) { + case ISO14443A_CMD_WUPA: + case ISO14443A_CMD_REQA: + MifareAuthState = masNone; + break; + default: + break; + } + } + + // get UID + if (MifareAuthState == masNone) { + if (cmdsize == 9 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && cmd[1] == 0x70) { + ClearAuthData(); + AuthData.uid = bytes_to_num(&cmd[2], 4); + } + if (cmdsize == 9 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && cmd[1] == 0x70) { + ClearAuthData(); + AuthData.uid = bytes_to_num(&cmd[2], 4); + } + } + + switch(MifareAuthState) { + case masNt: + if (cmdsize == 4 && isResponse) { + snprintf(exp,size,"AUTH: nt %s", (AuthData.first_auth) ? "" : "(enc)"); + MifareAuthState = masNrAr; + if (AuthData.first_auth) { + AuthData.nt = bytes_to_num(cmd, 4); + } else { + AuthData.nt_enc = bytes_to_num(cmd, 4); + AuthData.nt_enc_par = parity[0]; + } + return; + } else { + MifareAuthState = masError; + } + break; + case masNrAr: + if (cmdsize == 8 && !isResponse) { + snprintf(exp,size,"AUTH: nr ar (enc)"); + MifareAuthState = masAt; + AuthData.nr_enc = bytes_to_num(cmd, 4); + AuthData.ar_enc = bytes_to_num(&cmd[4], 4); + AuthData.ar_enc_par = parity[0] << 4; + return; + } else { + MifareAuthState = masError; + } + break; + case masAt: + if (cmdsize == 4 && isResponse) { + snprintf(exp,size,"AUTH: at (enc)"); + MifareAuthState = masAuthComplete; + AuthData.at_enc = bytes_to_num(cmd, 4); + AuthData.at_enc_par = parity[0]; + return; + } else { + MifareAuthState = masError; + } + break; + default: + break; + } + + if (!isResponse && ((MifareAuthState == masNone) || (MifareAuthState == masError))) + annotateIso14443a(exp, size, cmd, cmdsize); + +} + +bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen) { + static struct Crypto1State *traceCrypto1; + static uint64_t mfLastKey; + + *mfDataLen = 0; + + if (MifareAuthState == masAuthComplete) { + if (traceCrypto1) { + crypto1_destroy(traceCrypto1); + traceCrypto1 = NULL; + } + + MifareAuthState = masFirstData; + return false; + } + + if (cmdsize > 32) + return false; + + if (MifareAuthState == masFirstData) { + if (AuthData.first_auth) { + AuthData.ks2 = AuthData.ar_enc ^ prng_successor(AuthData.nt, 64); + AuthData.ks3 = AuthData.at_enc ^ prng_successor(AuthData.nt, 96); + + mfLastKey = GetCrypto1ProbableKey(&AuthData); + PrintAndLog(" | * | key | probable key:%012"PRIx64" Prng:%s ks2:%08x ks3:%08x | |", + mfLastKey, + validate_prng_nonce(AuthData.nt) ? "WEAK": "HARD", + AuthData.ks2, + AuthData.ks3); + + AuthData.first_auth = false; + + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); + } else { + if (traceCrypto1) { + crypto1_destroy(traceCrypto1); + traceCrypto1 = NULL; + } + + // check last used key + if (mfLastKey) { + if (NestedCheckKey(mfLastKey, &AuthData, cmd, cmdsize, parity)) { + PrintAndLog(" | * | key | last used key:%012"PRIx64" ks2:%08x ks3:%08x | |", + mfLastKey, + AuthData.ks2, + AuthData.ks3); + + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); + }; + } + + // check default keys + if (!traceCrypto1) { + for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ + if (NestedCheckKey(MifareDefaultKeys[defaultKeyCounter], &AuthData, cmd, cmdsize, parity)) { + PrintAndLog(" | * | key | default key:%012"PRIx64" ks2:%08x ks3:%08x | |", + MifareDefaultKeys[defaultKeyCounter], + AuthData.ks2, + AuthData.ks3); + + mfLastKey = MifareDefaultKeys[defaultKeyCounter]; + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); + break; + }; + } + } + + // nested + if (!traceCrypto1 && validate_prng_nonce(AuthData.nt)) { + uint32_t ntx = prng_successor(AuthData.nt, 90); + for (int i = 0; i < 16383; i++) { + ntx = prng_successor(ntx, 1); + if (NTParityChk(&AuthData, ntx)){ + + uint32_t ks2 = AuthData.ar_enc ^ prng_successor(ntx, 64); + uint32_t ks3 = AuthData.at_enc ^ prng_successor(ntx, 96); + struct Crypto1State *pcs = lfsr_recovery64(ks2, ks3); + memcpy(mfData, cmd, cmdsize); + mf_crypto1_decrypt(pcs, mfData, cmdsize, 0); + + crypto1_destroy(pcs); + if (CheckCrypto1Parity(cmd, cmdsize, mfData, parity) && CheckCrc14443(CRC_14443_A, mfData, cmdsize)) { + AuthData.ks2 = ks2; + AuthData.ks3 = ks3; + + AuthData.nt = ntx; + mfLastKey = GetCrypto1ProbableKey(&AuthData); + PrintAndLog(" | * | key | nested probable key:%012"PRIx64" ks2:%08x ks3:%08x | |", + mfLastKey, + AuthData.ks2, + AuthData.ks3); + + traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); + break; + } + } + } + } + + //hardnested + if (!traceCrypto1) { + printf("hardnested not implemented. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); + MifareAuthState = masError; + + /* TOO SLOW( needs to have more strong filter. with this filter - aprox 4 mln tests + uint32_t t = msclock(); + uint32_t t1 = t; + int n = 0; + for (uint32_t i = 0; i < 0xFFFFFFFF; i++) { + if (NTParityChk(&AuthData, i)){ + + uint32_t ks2 = AuthData.ar_enc ^ prng_successor(i, 64); + uint32_t ks3 = AuthData.at_enc ^ prng_successor(i, 96); + struct Crypto1State *pcs = lfsr_recovery64(ks2, ks3); + + + + + n++; + + if (!(n % 100000)) { + printf("delta=%d n=%d ks2=%x ks3=%x \n", msclock() - t1 , n, ks2, ks3); + t1 = msclock(); + } + + } + } + printf("delta=%d n=%d\n", msclock() - t, n); + */ + } + } + + + + MifareAuthState = masData; + } + + if (MifareAuthState == masData && traceCrypto1) { + memcpy(mfData, cmd, cmdsize); + mf_crypto1_decrypt(traceCrypto1, mfData, cmdsize, 0); + *mfDataLen = cmdsize; + } + + return *mfDataLen > 0; +} + +bool NTParityChk(TAuthData *ad, uint32_t ntx) { + if ( + (oddparity8(ntx >> 8 & 0xff) ^ (ntx & 0x01) ^ ((ad->nt_enc_par >> 5) & 0x01) ^ (ad->nt_enc & 0x01)) || + (oddparity8(ntx >> 16 & 0xff) ^ (ntx >> 8 & 0x01) ^ ((ad->nt_enc_par >> 6) & 0x01) ^ (ad->nt_enc >> 8 & 0x01)) || + (oddparity8(ntx >> 24 & 0xff) ^ (ntx >> 16 & 0x01) ^ ((ad->nt_enc_par >> 7) & 0x01) ^ (ad->nt_enc >> 16 & 0x01)) + ) + return false; + + uint32_t ar = prng_successor(ntx, 64); + if ( + (oddparity8(ar >> 8 & 0xff) ^ (ar & 0x01) ^ ((ad->ar_enc_par >> 5) & 0x01) ^ (ad->ar_enc & 0x01)) || + (oddparity8(ar >> 16 & 0xff) ^ (ar >> 8 & 0x01) ^ ((ad->ar_enc_par >> 6) & 0x01) ^ (ad->ar_enc >> 8 & 0x01)) || + (oddparity8(ar >> 24 & 0xff) ^ (ar >> 16 & 0x01) ^ ((ad->ar_enc_par >> 7) & 0x01) ^ (ad->ar_enc >> 16 & 0x01)) + ) + return false; + + uint32_t at = prng_successor(ntx, 96); + if ( + (oddparity8(ar & 0xff) ^ (at >> 24 & 0x01) ^ ((ad->ar_enc_par >> 4) & 0x01) ^ (ad->at_enc >> 24 & 0x01)) || + (oddparity8(at >> 8 & 0xff) ^ (at & 0x01) ^ ((ad->at_enc_par >> 5) & 0x01) ^ (ad->at_enc & 0x01)) || + (oddparity8(at >> 16 & 0xff) ^ (at >> 8 & 0x01) ^ ((ad->at_enc_par >> 6) & 0x01) ^ (ad->at_enc >> 8 & 0x01)) || + (oddparity8(at >> 24 & 0xff) ^ (at >> 16 & 0x01) ^ ((ad->at_enc_par >> 7) & 0x01) ^ (ad->at_enc >> 16 & 0x01)) + ) + return false; + + return true; +} + +bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, uint8_t *parity) { + uint8_t buf[32] = {0}; + struct Crypto1State *pcs; + + AuthData.ks2 = 0; + AuthData.ks3 = 0; + + pcs = crypto1_create(key); + uint32_t nt1 = crypto1_word(pcs, ad->nt_enc ^ ad->uid, 1) ^ ad->nt_enc; + uint32_t ar = prng_successor(nt1, 64); + uint32_t at = prng_successor(nt1, 96); + + crypto1_word(pcs, ad->nr_enc, 1); +// uint32_t nr1 = crypto1_word(pcs, ad->nr_enc, 1) ^ ad->nr_enc; // if needs deciphered nr + uint32_t ar1 = crypto1_word(pcs, 0, 0) ^ ad->ar_enc; + uint32_t at1 = crypto1_word(pcs, 0, 0) ^ ad->at_enc; + + if (!(ar == ar1 && at == at1 && NTParityChk(ad, nt1))) { + crypto1_destroy(pcs); + return false; + } + + memcpy(buf, cmd, cmdsize); + mf_crypto1_decrypt(pcs, buf, cmdsize, 0); + + crypto1_destroy(pcs); + + if (!CheckCrypto1Parity(cmd, cmdsize, buf, parity)) + return false; + + if(!CheckCrc14443(CRC_14443_A, buf, cmdsize)) + return false; + + AuthData.nt = nt1; + AuthData.ks2 = AuthData.ar_enc ^ ar; + AuthData.ks3 = AuthData.at_enc ^ at; + + return true; +} + +bool CheckCrypto1Parity(uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, uint8_t *parity_enc) { + for (int i = 0; i < cmdsize - 1; i++) { + if (oddparity8(cmd[i]) ^ (cmd[i + 1] & 0x01) ^ ((parity_enc[i / 8] >> (7 - i % 8)) & 0x01) ^ (cmd_enc[i + 1] & 0x01)) + return false; + } + + return true; +} + +uint64_t GetCrypto1ProbableKey(TAuthData *ad) { + struct Crypto1State *revstate = lfsr_recovery64(ad->ks2, ad->ks3); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, ad->nr_enc, 1); + lfsr_rollback_word(revstate, ad->uid ^ ad->nt, 0); + + uint64_t lfsr = 0; + crypto1_get_lfsr(revstate, &lfsr); + crypto1_destroy(revstate); + + return lfsr; +} diff --git a/client/cmdhflist.h b/client/cmdhflist.h new file mode 100644 index 00000000..8f289b48 --- /dev/null +++ b/client/cmdhflist.h @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Merlok - 2017 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Command: hf mf list. It shows data from arm buffer. +//----------------------------------------------------------------------------- +#ifndef CMDHFLIST_H +#define CMDHFLIST_H + +#include +#include +#include + +typedef struct { + uint32_t uid; // UID + uint32_t nt; // tag challenge + uint32_t nt_enc; // encrypted tag challenge + uint8_t nt_enc_par; // encrypted tag challenge parity + uint32_t nr_enc; // encrypted reader challenge + uint32_t ar_enc; // encrypted reader response + uint8_t ar_enc_par; // encrypted reader response parity + uint32_t at_enc; // encrypted tag response + uint8_t at_enc_par; // encrypted tag response parity + bool first_auth; // is first authentication + uint32_t ks2; // ar ^ ar_enc + uint32_t ks3; // at ^ at_enc +} TAuthData; +extern void ClearAuthData(); + +extern uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len); +extern uint8_t mifare_CRC_check(bool isResponse, uint8_t* data, uint8_t len); +extern void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); +extern void annotateIso15693(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); +extern void annotateTopaz(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); +extern void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); +extern void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize); +extern void annotateMifare(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize, uint8_t* parity, uint8_t paritysize, bool isResponse); +extern bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen); +extern bool NTParityChk(TAuthData *ad, uint32_t ntx); +extern bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, uint8_t *parity); +extern bool CheckCrypto1Parity(uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, uint8_t *parity_enc); +extern uint64_t GetCrypto1ProbableKey(TAuthData *ad); + +#endif // CMDHFLIST diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 90ebc27b..83180e7d 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -15,9 +15,10 @@ #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "cmdmain.h" #include "cmdhfmfhard.h" +#include "parity.h" #include "util.h" #include "util_posix.h" #include "usb_cmd.h" @@ -25,6 +26,10 @@ #include "mifarehost.h" #include "mifare.h" #include "mfkey.h" +#include "hardnested/hardnested_bf_core.h" +#include "cliparser/cliparser.h" +#include "cmdhf14a.h" +#include "mifare4.h" #define NESTED_SECTOR_RETRY 10 // how often we try mfested() until we give up @@ -222,6 +227,28 @@ uint8_t NumBlocksPerSector(uint8_t sectorNo) } } +static int ParamCardSizeSectors(const char c) { + int numBlocks = 16; + switch (c) { + case '0' : numBlocks = 5; break; + case '2' : numBlocks = 32; break; + case '4' : numBlocks = 40; break; + default: numBlocks = 16; + } + return numBlocks; +} + +static int ParamCardSizeBlocks(const char c) { + int numBlocks = 16 * 4; + switch (c) { + case '0' : numBlocks = 5 * 4; break; + case '2' : numBlocks = 32 * 4; break; + case '4' : numBlocks = 32 * 4 + 8 * 16; break; + default: numBlocks = 16 * 4; + } + return numBlocks; +} + int CmdHF14AMfDump(const char *Cmd) { uint8_t sectorNo, blockNo; @@ -238,14 +265,7 @@ int CmdHF14AMfDump(const char *Cmd) UsbCommand resp; char cmdp = param_getchar(Cmd, 0); - switch (cmdp) { - case '0' : numSectors = 5; break; - case '1' : - case '\0': numSectors = 16; break; - case '2' : numSectors = 32; break; - case '4' : numSectors = 40; break; - default: numSectors = 16; - } + numSectors = ParamCardSizeSectors(cmdp); if (strlen(Cmd) > 1 || cmdp == 'h' || cmdp == 'H') { PrintAndLog("Usage: hf mf dump [card memory]"); @@ -509,12 +529,33 @@ int CmdHF14AMfRestore(const char *Cmd) return 0; } +//---------------------------------------------- +// Nested +//---------------------------------------------- -typedef struct { - uint64_t Key[2]; - int foundKey[2]; -} sector_t; +static void parseParamTDS(const char *Cmd, const uint8_t indx, bool *paramT, bool *paramD, uint8_t *timeout) { + char ctmp3[3] = {0}; + int len = param_getlength(Cmd, indx); + if (len > 0 && len < 4){ + param_getstr(Cmd, indx, ctmp3, sizeof(ctmp3)); + + *paramT |= (ctmp3[0] == 't' || ctmp3[0] == 'T'); + *paramD |= (ctmp3[0] == 'd' || ctmp3[0] == 'D'); + bool paramS1 = *paramT || *paramD; + // slow and very slow + if (ctmp3[0] == 's' || ctmp3[0] == 'S' || ctmp3[1] == 's' || ctmp3[1] == 'S') { + *timeout = 11; // slow + + if (!paramS1 && (ctmp3[1] == 's' || ctmp3[1] == 'S')) { + *timeout = 53; // very slow + } + if (paramS1 && (ctmp3[2] == 's' || ctmp3[2] == 'S')) { + *timeout = 53; // very slow + } + } + } +} int CmdHF14AMfNested(const char *Cmd) { @@ -526,10 +567,14 @@ int CmdHF14AMfNested(const char *Cmd) uint8_t trgKeyType = 0; uint8_t SectorsCnt = 0; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t keyBlock[14*6]; + uint8_t keyBlock[MifareDefaultKeysSize * 6]; uint64_t key64 = 0; - bool transferToEml = false; + // timeout in units. (ms * 106)/10 or us*0.0106 + uint8_t btimeout14a = MF_CHKKEYS_DEFTIMEOUT; // fast by default + + bool autosearchKey = false; + bool transferToEml = false; bool createDumpFile = false; FILE *fkeys; uint8_t standart[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; @@ -539,67 +584,90 @@ int CmdHF14AMfNested(const char *Cmd) if (strlen(Cmd)<3) { PrintAndLog("Usage:"); - PrintAndLog(" all sectors: hf mf nested [t,d]"); + PrintAndLog(" all sectors: hf mf nested [t|d|s|ss]"); + PrintAndLog(" all sectors autosearch key: hf mf nested * [t|d|s|ss]"); PrintAndLog(" one sector: hf mf nested o "); PrintAndLog(" [t]"); + PrintAndLog(" "); PrintAndLog("card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); - PrintAndLog("t - transfer keys into emulator memory"); - PrintAndLog("d - write keys to binary file"); + PrintAndLog("t - transfer keys to emulator memory"); + PrintAndLog("d - write keys to binary file dumpkeys.bin"); + PrintAndLog("s - Slow (1ms) check keys (required by some non standard cards)"); + PrintAndLog("ss - Very slow (5ms) check keys"); PrintAndLog(" "); PrintAndLog(" sample1: hf mf nested 1 0 A FFFFFFFFFFFF "); PrintAndLog(" sample2: hf mf nested 1 0 A FFFFFFFFFFFF t "); PrintAndLog(" sample3: hf mf nested 1 0 A FFFFFFFFFFFF d "); PrintAndLog(" sample4: hf mf nested o 0 A FFFFFFFFFFFF 4 A"); + PrintAndLog(" sample5: hf mf nested 1 * t"); + PrintAndLog(" sample6: hf mf nested 1 * ss"); return 0; } + // cmdp = param_getchar(Cmd, 0); - blockNo = param_get8(Cmd, 1); - ctmp = param_getchar(Cmd, 2); - - if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { - PrintAndLog("Key type must be A or B"); - return 1; - } - - if (ctmp != 'A' && ctmp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 3, key, 12)) { - PrintAndLog("Key must include 12 HEX symbols"); - return 1; - } - if (cmdp == 'o' || cmdp == 'O') { cmdp = 'o'; - trgBlockNo = param_get8(Cmd, 4); - ctmp = param_getchar(Cmd, 5); + SectorsCnt = 1; + } else { + SectorsCnt = ParamCardSizeSectors(cmdp); + } + + // . number or autosearch key (*) + if (param_getchar(Cmd, 1) == '*') { + autosearchKey = true; + + parseParamTDS(Cmd, 2, &transferToEml, &createDumpFile, &btimeout14a); + + PrintAndLog("--nested. sectors:%2d, block no:*, eml:%c, dmp=%c checktimeout=%d us", + SectorsCnt, transferToEml?'y':'n', createDumpFile?'y':'n', ((int)btimeout14a * 10000) / 106); + } else { + blockNo = param_get8(Cmd, 1); + + ctmp = param_getchar(Cmd, 2); if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { - PrintAndLog("Target key type must be A or B"); + PrintAndLog("Key type must be A or B"); return 1; } - if (ctmp != 'A' && ctmp != 'a') - trgKeyType = 1; - } else { - switch (cmdp) { - case '0': SectorsCnt = 05; break; - case '1': SectorsCnt = 16; break; - case '2': SectorsCnt = 32; break; - case '4': SectorsCnt = 40; break; - default: SectorsCnt = 16; + if (ctmp != 'A' && ctmp != 'a') + keyType = 1; + + if (param_gethex(Cmd, 3, key, 12)) { + PrintAndLog("Key must include 12 HEX symbols"); + return 1; } + + // check if we can authenticate to sector + res = mfCheckKeys(blockNo, keyType, true, 1, key, &key64); + if (res) { + PrintAndLog("Can't authenticate to block:%3d key type:%c key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); + return 3; + } + + // one sector nested + if (cmdp == 'o') { + trgBlockNo = param_get8(Cmd, 4); + + ctmp = param_getchar(Cmd, 5); + if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { + PrintAndLog("Target key type must be A or B"); + return 1; + } + if (ctmp != 'A' && ctmp != 'a') + trgKeyType = 1; + + parseParamTDS(Cmd, 6, &transferToEml, &createDumpFile, &btimeout14a); + } else { + parseParamTDS(Cmd, 4, &transferToEml, &createDumpFile, &btimeout14a); + } + + PrintAndLog("--nested. sectors:%2d, block no:%3d, key type:%c, eml:%c, dmp=%c checktimeout=%d us", + SectorsCnt, blockNo, keyType?'B':'A', transferToEml?'y':'n', createDumpFile?'y':'n', ((int)btimeout14a * 10000) / 106); } - ctmp = param_getchar(Cmd, 4); - if (ctmp == 't' || ctmp == 'T') transferToEml = true; - else if (ctmp == 'd' || ctmp == 'D') createDumpFile = true; - - ctmp = param_getchar(Cmd, 6); - transferToEml |= (ctmp == 't' || ctmp == 'T'); - transferToEml |= (ctmp == 'd' || ctmp == 'D'); - - if (cmdp == 'o') { + // one-sector nested + if (cmdp == 'o') { // ------------------------------------ one sector working PrintAndLog("--target block no:%3d, target key type:%c ", trgBlockNo, trgKeyType?'B':'A'); int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, true); if (isOK) { @@ -619,9 +687,9 @@ int CmdHF14AMfNested(const char *Cmd) if (transferToEml) { uint8_t sectortrailer; if (trgBlockNo < 32*4) { // 4 block sector - sectortrailer = (trgBlockNo & 0x03) + 3; + sectortrailer = trgBlockNo | 0x03; } else { // 16 block sector - sectortrailer = (trgBlockNo & 0x0f) + 15; + sectortrailer = trgBlockNo | 0x0f; } mfEmlGetMem(keyBlock, sectortrailer, 1); @@ -630,6 +698,7 @@ int CmdHF14AMfNested(const char *Cmd) else num_to_bytes(key64, 6, &keyBlock[10]); mfEmlSetMem(keyBlock, sectortrailer, 1); + PrintAndLog("Key transferred to emulator memory."); } } else { PrintAndLog("No valid key found"); @@ -643,33 +712,37 @@ int CmdHF14AMfNested(const char *Cmd) if (e_sector == NULL) return 1; //test current key and additional standard keys first - memcpy(keyBlock, key, 6); - num_to_bytes(0xffffffffffff, 6, (uint8_t*)(keyBlock + 1 * 6)); - num_to_bytes(0x000000000000, 6, (uint8_t*)(keyBlock + 2 * 6)); - num_to_bytes(0xa0a1a2a3a4a5, 6, (uint8_t*)(keyBlock + 3 * 6)); - num_to_bytes(0xb0b1b2b3b4b5, 6, (uint8_t*)(keyBlock + 4 * 6)); - num_to_bytes(0xaabbccddeeff, 6, (uint8_t*)(keyBlock + 5 * 6)); - num_to_bytes(0x4d3a99c351dd, 6, (uint8_t*)(keyBlock + 6 * 6)); - num_to_bytes(0x1a982c7e459a, 6, (uint8_t*)(keyBlock + 7 * 6)); - num_to_bytes(0xd3f7d3f7d3f7, 6, (uint8_t*)(keyBlock + 8 * 6)); - num_to_bytes(0x714c5c886e97, 6, (uint8_t*)(keyBlock + 9 * 6)); - num_to_bytes(0x587ee5f9350f, 6, (uint8_t*)(keyBlock + 10 * 6)); - num_to_bytes(0xa0478cc39091, 6, (uint8_t*)(keyBlock + 11 * 6)); - num_to_bytes(0x533cb6c723f6, 6, (uint8_t*)(keyBlock + 12 * 6)); - num_to_bytes(0x8fd0a4f256e9, 6, (uint8_t*)(keyBlock + 13 * 6)); + for (int defaultKeyCounter = 0; defaultKeyCounter < MifareDefaultKeysSize; defaultKeyCounter++){ + num_to_bytes(MifareDefaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); + } PrintAndLog("Testing known keys. Sector count=%d", SectorsCnt); - for (i = 0; i < SectorsCnt; i++) { - for (j = 0; j < 2; j++) { - if (e_sector[i].foundKey[j]) continue; - - res = mfCheckKeys(FirstBlockOfSector(i), j, true, 6, keyBlock, &key64); - - if (!res) { - e_sector[i].Key[j] = key64; - e_sector[i].foundKey[j] = 1; + mfCheckKeysSec(SectorsCnt, 2, btimeout14a, true, MifareDefaultKeysSize, keyBlock, e_sector); + + // get known key from array + bool keyFound = false; + if (autosearchKey) { + for (i = 0; i < SectorsCnt; i++) { + for (j = 0; j < 2; j++) { + if (e_sector[i].foundKey[j]) { + // get known key + blockNo = i * 4; + keyType = j; + num_to_bytes(e_sector[i].Key[j], 6, key); + keyFound = true; + break; + } } + if (keyFound) break; + } + + // Can't found a key.... + if (!keyFound) { + PrintAndLog("Can't found any of the known keys."); + free(e_sector); + return 4; } + PrintAndLog("--auto key. block no:%3d, key type:%c key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); } // nested sectors @@ -702,15 +775,19 @@ int CmdHF14AMfNested(const char *Cmd) PrintAndLog("Found valid key:%012" PRIx64, key64); e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = key64; + + // try to check this key as a key to the other sectors + mfCheckKeysSec(SectorsCnt, 2, btimeout14a, true, 1, keyBlock, e_sector); } } } } - printf("Time in nested: %1.3f (%1.3f sec per key)\n\n", ((float)(msclock() - msclock1))/1000.0, ((float)(msclock() - msclock1))/iterations/1000.0); - - PrintAndLog("-----------------------------------------------\nIterations count: %d\n\n", iterations); - //print them + // print nested statistic + PrintAndLog("\n\n-----------------------------------------------\nNested statistic:\nIterations count: %d", iterations); + PrintAndLog("Time in nested: %1.3f (%1.3f sec per key)", ((float)(msclock() - msclock1))/1000.0, ((float)(msclock() - msclock1))/iterations/1000.0); + + // print result PrintAndLog("|---|----------------|---|----------------|---|"); PrintAndLog("|sec|key A |res|key B |res|"); PrintAndLog("|---|----------------|---|----------------|---|"); @@ -720,7 +797,7 @@ int CmdHF14AMfNested(const char *Cmd) } PrintAndLog("|---|----------------|---|----------------|---|"); - // transfer them to the emulator + // transfer keys to the emulator memory if (transferToEml) { for (i = 0; i < SectorsCnt; i++) { mfEmlGetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); @@ -730,6 +807,7 @@ int CmdHF14AMfNested(const char *Cmd) num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); mfEmlSetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); } + PrintAndLog("Keys transferred to emulator memory."); } // Create dump file @@ -789,6 +867,13 @@ int CmdHF14AMfNestedHard(const char *Cmd) PrintAndLog(" w: Acquire nonces and write them to binary file nonces.bin"); PrintAndLog(" s: Slower acquisition (required by some non standard cards)"); PrintAndLog(" r: Read nonces.bin and start attack"); + PrintAndLog(" iX: set type of SIMD instructions. Without this flag programs autodetect it."); + PrintAndLog(" i5: AVX512"); + PrintAndLog(" i2: AVX2"); + PrintAndLog(" ia: AVX"); + PrintAndLog(" is: SSE2"); + PrintAndLog(" im: MMX"); + PrintAndLog(" in: none (use CPU regular instruction set)"); PrintAndLog(" "); PrintAndLog(" sample1: hf mf hardnested 0 A FFFFFFFFFFFF 4 A"); PrintAndLog(" sample2: hf mf hardnested 0 A FFFFFFFFFFFF 4 A w"); @@ -807,15 +892,20 @@ int CmdHF14AMfNestedHard(const char *Cmd) int tests = 0; + uint16_t iindx = 0; if (ctmp == 'R' || ctmp == 'r') { nonce_file_read = true; + iindx = 1; if (!param_gethex(Cmd, 1, trgkey, 12)) { know_target_key = true; + iindx = 2; } } else if (ctmp == 'T' || ctmp == 't') { tests = param_get32ex(Cmd, 1, 100, 10); + iindx = 2; if (!param_gethex(Cmd, 2, trgkey, 12)) { know_target_key = true; + iindx = 3; } } else { blockNo = param_get8(Cmd, 0); @@ -849,19 +939,54 @@ int CmdHF14AMfNestedHard(const char *Cmd) know_target_key = true; i++; } + iindx = i; while ((ctmp = param_getchar(Cmd, i))) { if (ctmp == 's' || ctmp == 'S') { slow = true; } else if (ctmp == 'w' || ctmp == 'W') { nonce_file_write = true; + } else if (param_getlength(Cmd, i) == 2 && ctmp == 'i') { + iindx = i; } else { - PrintAndLog("Possible options are w and/or s"); + PrintAndLog("Possible options are w , s and/or iX"); return 1; } i++; } } + + SetSIMDInstr(SIMD_AUTO); + if (iindx > 0) { + while ((ctmp = param_getchar(Cmd, iindx))) { + if (param_getlength(Cmd, iindx) == 2 && ctmp == 'i') { + switch(param_getchar_indx(Cmd, 1, iindx)) { + case '5': + SetSIMDInstr(SIMD_AVX512); + break; + case '2': + SetSIMDInstr(SIMD_AVX2); + break; + case 'a': + SetSIMDInstr(SIMD_AVX); + break; + case 's': + SetSIMDInstr(SIMD_SSE2); + break; + case 'm': + SetSIMDInstr(SIMD_MMX); + break; + case 'n': + SetSIMDInstr(SIMD_NONE); + break; + default: + PrintAndLog("Unknown SIMD type. %c", param_getchar_indx(Cmd, 1, iindx)); + return 1; + } + } + iindx++; + } + } PrintAndLog("--target block no:%3d, target key type:%c, known target key: 0x%02x%02x%02x%02x%02x%02x%s, file action: %s, Slow: %s, Tests: %d ", trgBlockNo, @@ -890,14 +1015,18 @@ int CmdHF14AMfNestedHard(const char *Cmd) int CmdHF14AMfChk(const char *Cmd) { if (strlen(Cmd)<3) { - PrintAndLog("Usage: hf mf chk |<*card memory> [t|d] [] []"); + PrintAndLog("Usage: hf mf chk |<*card memory> [t|d|s|ss] [] []"); PrintAndLog(" * - all sectors"); PrintAndLog("card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); PrintAndLog("d - write keys to binary file\n"); PrintAndLog("t - write keys to emulator memory"); + PrintAndLog("s - slow execute. timeout 1ms"); + PrintAndLog("ss- very slow execute. timeout 5ms"); PrintAndLog(" sample: hf mf chk 0 A 1234567890ab keys.dic"); PrintAndLog(" hf mf chk *1 ? t"); PrintAndLog(" hf mf chk *1 ? d"); + PrintAndLog(" hf mf chk *1 ? s"); + PrintAndLog(" hf mf chk *1 ? dss"); return 0; } @@ -910,75 +1039,82 @@ int CmdHF14AMfChk(const char *Cmd) int i, res; int keycnt = 0; char ctmp = 0x00; + int clen = 0; + char ctmp3[3] = {0x00}; uint8_t blockNo = 0; - uint8_t SectorsCnt = 1; + uint8_t SectorsCnt = 0; uint8_t keyType = 0; uint64_t key64 = 0; + uint32_t timeout14a = 0; // timeout in us + bool param3InUse = false; int transferToEml = 0; int createDumpFile = 0; + + sector_t *e_sector = NULL; keyBlock = calloc(stKeyBlock, 6); if (keyBlock == NULL) return 1; - uint64_t defaultKeys[] = - { - 0xffffffffffff, // Default key (first key used by program if no user defined key) - 0x000000000000, // Blank key - 0xa0a1a2a3a4a5, // NFCForum MAD key - 0xb0b1b2b3b4b5, - 0xaabbccddeeff, - 0x4d3a99c351dd, - 0x1a982c7e459a, - 0xd3f7d3f7d3f7, - 0x714c5c886e97, - 0x587ee5f9350f, - 0xa0478cc39091, - 0x533cb6c723f6, - 0x8fd0a4f256e9 - }; - int defaultKeysSize = sizeof(defaultKeys) / sizeof(uint64_t); - - for (int defaultKeyCounter = 0; defaultKeyCounter < defaultKeysSize; defaultKeyCounter++) - { - num_to_bytes(defaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); + int defaultKeysSize = MifareDefaultKeysSize; + for (int defaultKeyCounter = 0; defaultKeyCounter < defaultKeysSize; defaultKeyCounter++){ + num_to_bytes(MifareDefaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6)); } if (param_getchar(Cmd, 0)=='*') { - blockNo = 3; - switch(param_getchar(Cmd+1, 0)) { - case '0': SectorsCnt = 5; break; - case '1': SectorsCnt = 16; break; - case '2': SectorsCnt = 32; break; - case '4': SectorsCnt = 40; break; - default: SectorsCnt = 16; - } + SectorsCnt = ParamCardSizeSectors(param_getchar(Cmd + 1, 0)); } else blockNo = param_get8(Cmd, 0); ctmp = param_getchar(Cmd, 1); - switch (ctmp) { - case 'a': case 'A': - keyType = !0; - break; - case 'b': case 'B': - keyType = !1; - break; - case '?': - keyType = 2; - break; - default: - PrintAndLog("Key type must be A , B or ?"); - free(keyBlock); - return 1; - }; + clen = param_getlength(Cmd, 1); + if (clen == 1) { + switch (ctmp) { + case 'a': case 'A': + keyType = 0; + break; + case 'b': case 'B': + keyType = 1; + break; + case '?': + keyType = 2; + break; + default: + PrintAndLog("Key type must be A , B or ?"); + free(keyBlock); + return 1; + }; + } + // transfer to emulator & create dump file ctmp = param_getchar(Cmd, 2); - if (ctmp == 't' || ctmp == 'T') transferToEml = 1; - else if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1; + clen = param_getlength(Cmd, 2); + if (clen == 1 && (ctmp == 't' || ctmp == 'T')) transferToEml = 1; + if (clen == 1 && (ctmp == 'd' || ctmp == 'D')) createDumpFile = 1; + + param3InUse = transferToEml | createDumpFile; + + timeout14a = 500; // fast by default + // double parameters - ts, ds + clen = param_getlength(Cmd, 2); + if (clen == 2 || clen == 3){ + param_getstr(Cmd, 2, ctmp3, sizeof(ctmp3)); + ctmp = ctmp3[1]; + } + //parse + if (ctmp == 's' || ctmp == 'S') { + timeout14a = 1000; // slow + if (!param3InUse && clen == 2 && (ctmp3[1] == 's' || ctmp3[1] == 'S')) { + timeout14a = 5000; // very slow + } + if (param3InUse && clen == 3 && (ctmp3[2] == 's' || ctmp3[2] == 'S')) { + timeout14a = 5000; // very slow + } + param3InUse = true; + } - for (i = transferToEml || createDumpFile; param_getchar(Cmd, 2 + i); i++) { + for (i = param3InUse; param_getchar(Cmd, 2 + i); i++) { if (!param_gethex(Cmd, 2 + i, keyBlock + 6 * keycnt, 12)) { if ( stKeyBlock - keycnt < 2) { p = realloc(keyBlock, 6*(stKeyBlock+=10)); @@ -995,7 +1131,7 @@ int CmdHF14AMfChk(const char *Cmd) keycnt++; } else { // May be a dic file - if ( param_getstr(Cmd, 2 + i,filename) >= FILE_PATH_SIZE ) { + if ( param_getstr(Cmd, 2 + i, filename, sizeof(filename)) >= FILE_PATH_SIZE ) { PrintAndLog("File name too long"); free(keyBlock); return 2; @@ -1010,7 +1146,7 @@ int CmdHF14AMfChk(const char *Cmd) if( buf[0]=='#' ) continue; //The line start with # is comment, skip - if (!isxdigit(buf[0])){ + if (!isxdigit((unsigned char)buf[0])){ PrintAndLog("File content error. '%s' must include 12 HEX symbols",buf); continue; } @@ -1043,6 +1179,7 @@ int CmdHF14AMfChk(const char *Cmd) } } + // fill with default keys if (keycnt == 0) { PrintAndLog("No key specified, trying default keys"); for (;keycnt < defaultKeysSize; keycnt++) @@ -1052,47 +1189,87 @@ int CmdHF14AMfChk(const char *Cmd) } // initialize storage for found keys - bool validKey[2][40]; - uint8_t foundKey[2][40][6]; - for (uint16_t t = 0; t < 2; t++) { + e_sector = calloc(SectorsCnt, sizeof(sector_t)); + if (e_sector == NULL) { + free(keyBlock); + return 1; + } + for (uint8_t keyAB = 0; keyAB < 2; keyAB++) { for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { - validKey[t][sectorNo] = false; - for (uint16_t i = 0; i < 6; i++) { - foundKey[t][sectorNo][i] = 0xff; - } + e_sector[sectorNo].Key[keyAB] = 0xffffffffffff; + e_sector[sectorNo].foundKey[keyAB] = 0; } } + printf("\n"); - for ( int t = !keyType; t < 2; keyType==2?(t++):(t=2) ) { - int b=blockNo; - for (int i = 0; i < SectorsCnt; ++i) { - PrintAndLog("--sector:%2d, block:%3d, key type:%C, key count:%2d ", i, b, t?'B':'A', keycnt); - uint32_t max_keys = keycnt>USB_CMD_DATA_SIZE/6?USB_CMD_DATA_SIZE/6:keycnt; + bool foundAKey = false; + uint32_t max_keys = keycnt > USB_CMD_DATA_SIZE / 6 ? USB_CMD_DATA_SIZE / 6 : keycnt; + if (SectorsCnt) { + PrintAndLog("To cancel this operation press the button on the proxmark..."); + printf("--"); + for (uint32_t c = 0; c < keycnt; c += max_keys) { + + uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; + res = mfCheckKeysSec(SectorsCnt, keyType, timeout14a * 1.06 / 100, true, size, &keyBlock[6 * c], e_sector); // timeout is (ms * 106)/10 or us*0.0106 + + if (res != 1) { + if (!res) { + printf("o"); + foundAKey = true; + } else { + printf("."); + } + } else { + printf("\n"); + PrintAndLog("Command execute timeout"); + } + } + } else { + int keyAB = keyType; + do { for (uint32_t c = 0; c < keycnt; c+=max_keys) { - uint32_t size = keycnt-c>max_keys?max_keys:keycnt-c; - res = mfCheckKeys(b, t, true, size, &keyBlock[6*c], &key64); + + uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; + res = mfCheckKeys(blockNo, keyAB & 0x01, true, size, &keyBlock[6 * c], &key64); + if (res != 1) { if (!res) { - PrintAndLog("Found valid key:[%012" PRIx64 "]",key64); - num_to_bytes(key64, 6, foundKey[t][i]); - validKey[t][i] = true; + PrintAndLog("Found valid key:[%d:%c]%012" PRIx64, blockNo, (keyAB & 0x01)?'B':'A', key64); + foundAKey = true; } } else { PrintAndLog("Command execute timeout"); } } - b<127?(b+=4):(b+=16); - } + } while(--keyAB > 0); } - + + // print result + if (foundAKey) { + if (SectorsCnt) { + PrintAndLog(""); + PrintAndLog("|---|----------------|---|----------------|---|"); + PrintAndLog("|sec|key A |res|key B |res|"); + PrintAndLog("|---|----------------|---|----------------|---|"); + for (i = 0; i < SectorsCnt; i++) { + PrintAndLog("|%03d| %012" PRIx64 " | %d | %012" PRIx64 " | %d |", i, + e_sector[i].Key[0], e_sector[i].foundKey[0], e_sector[i].Key[1], e_sector[i].foundKey[1]); + } + PrintAndLog("|---|----------------|---|----------------|---|"); + } + } else { + PrintAndLog(""); + PrintAndLog("No valid keys found."); + } + if (transferToEml) { uint8_t block[16]; for (uint16_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { - if (validKey[0][sectorNo] || validKey[1][sectorNo]) { + if (e_sector[sectorNo].foundKey[0] || e_sector[sectorNo].foundKey[1]) { mfEmlGetMem(block, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); for (uint16_t t = 0; t < 2; t++) { - if (validKey[t][sectorNo]) { - memcpy(block + t*10, foundKey[t][sectorNo], 6); + if (e_sector[sectorNo].foundKey[t]) { + num_to_bytes(e_sector[sectorNo].Key[t], 6, block + t * 10); } } mfEmlSetMem(block, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); @@ -1105,16 +1282,22 @@ int CmdHF14AMfChk(const char *Cmd) FILE *fkeys = fopen("dumpkeys.bin","wb"); if (fkeys == NULL) { PrintAndLog("Could not create file dumpkeys.bin"); + free(e_sector); free(keyBlock); return 1; } - for (uint16_t t = 0; t < 2; t++) { - fwrite(foundKey[t], 1, 6*SectorsCnt, fkeys); + uint8_t mkey[6]; + for (uint8_t t = 0; t < 2; t++) { + for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { + num_to_bytes(e_sector[sectorNo].Key[t], 6, mkey); + fwrite(mkey, 1, 6, fkeys); + } } fclose(fkeys); PrintAndLog("Found keys have been dumped to file dumpkeys.bin. 0xffffffffffff has been inserted for unknown keys."); } + free(e_sector); free(keyBlock); PrintAndLog(""); return 0; @@ -1274,7 +1457,7 @@ int CmdHF14AMf1kSim(const char *Cmd) { break; case 'f': case 'F': - len = param_getstr(Cmd, cmdp+1, filename); + len = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (len < 1) { PrintAndLog("error no filename found"); return 0; @@ -1509,10 +1692,7 @@ int CmdHF14AMfESet(const char *Cmd) } // 1 - blocks count - UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNo, 1, 0}}; - memcpy(c.d.asBytes, memBlock, 16); - SendCommand(&c); - return 0; + return mfEmlSetMem(memBlock, blockNo, 1); } @@ -1550,7 +1730,7 @@ int CmdHF14AMfELoad(const char *Cmd) } } - len = param_getstr(Cmd,nameParamNo,filename); + len = param_getstr(Cmd,nameParamNo,filename,sizeof(filename)); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; @@ -1649,7 +1829,7 @@ int CmdHF14AMfESave(const char *Cmd) } } - len = param_getstr(Cmd,nameParamNo,filename); + len = param_getstr(Cmd,nameParamNo,filename,sizeof(filename)); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; @@ -1731,7 +1911,7 @@ int CmdHF14AMfECFill(const char *Cmd) default: numSectors = 16; } - printf("--params: numSectors: %d, keyType:%d", numSectors, keyType); + printf("--params: numSectors: %d, keyType:%d\n", numSectors, keyType); UsbCommand c = {CMD_MIFARE_EML_CARDLOAD, {numSectors, keyType, 0}}; SendCommand(&c); return 0; @@ -1783,65 +1963,69 @@ int CmdHF14AMfEKeyPrn(const char *Cmd) int CmdHF14AMfCSetUID(const char *Cmd) { - uint8_t wipeCard = 0; uint8_t uid[8] = {0x00}; uint8_t oldUid[8] = {0x00}; uint8_t atqa[2] = {0x00}; uint8_t sak[1] = {0x00}; - uint8_t atqaPresent = 1; + uint8_t atqaPresent = 0; int res; - char ctmp; - int argi=0; - if (strlen(Cmd) < 1 || param_getchar(Cmd, argi) == 'h') { - PrintAndLog("Usage: hf mf csetuid [ATQA 4 hex symbols SAK 2 hex symbols] [w]"); - PrintAndLog("sample: hf mf csetuid 01020304"); - PrintAndLog("sample: hf mf csetuid 01020304 0004 08 w"); - PrintAndLog("Set UID, ATQA, and SAK for magic Chinese card (only works with such cards)"); - PrintAndLog("If you also want to wipe the card then add 'w' at the end of the command line."); - return 0; - } - - if (param_getchar(Cmd, argi) && param_gethex(Cmd, argi, uid, 8)) { + uint8_t needHelp = 0; + char cmdp = 1; + + if (param_getchar(Cmd, 0) && param_gethex(Cmd, 0, uid, 8)) { PrintAndLog("UID must include 8 HEX symbols"); return 1; } - argi++; - ctmp = param_getchar(Cmd, argi); - if (ctmp == 'w' || ctmp == 'W') { - wipeCard = 1; - atqaPresent = 0; - } - - if (atqaPresent) { - if (param_getchar(Cmd, argi)) { - if (param_gethex(Cmd, argi, atqa, 4)) { - PrintAndLog("ATQA must include 4 HEX symbols"); - return 1; - } - argi++; - if (!param_getchar(Cmd, argi) || param_gethex(Cmd, argi, sak, 2)) { - PrintAndLog("SAK must include 2 HEX symbols"); - return 1; - } - argi++; - } else - atqaPresent = 0; - } - - if(!wipeCard) { - ctmp = param_getchar(Cmd, argi); - if (ctmp == 'w' || ctmp == 'W') { - wipeCard = 1; + if (param_getlength(Cmd, 1) > 1 && param_getlength(Cmd, 2) > 1) { + atqaPresent = 1; + cmdp = 3; + + if (param_gethex(Cmd, 1, atqa, 4)) { + PrintAndLog("ATQA must include 4 HEX symbols"); + return 1; + } + + if (param_gethex(Cmd, 2, sak, 2)) { + PrintAndLog("SAK must include 2 HEX symbols"); + return 1; } } - PrintAndLog("--wipe card:%s uid:%s", (wipeCard)?"YES":"NO", sprint_hex(uid, 4)); + while(param_getchar(Cmd, cmdp) != 0x00) + { + switch(param_getchar(Cmd, cmdp)) + { + case 'h': + case 'H': + needHelp = 1; + break; + default: + PrintAndLog("ERROR: Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + needHelp = 1; + break; + } + cmdp++; + } - res = mfCSetUID(uid, (atqaPresent)?atqa:NULL, (atqaPresent)?sak:NULL, oldUid, wipeCard); + if (strlen(Cmd) < 1 || needHelp) { + PrintAndLog(""); + PrintAndLog("Usage: hf mf csetuid [ATQA 4 hex symbols SAK 2 hex symbols]"); + PrintAndLog("sample: hf mf csetuid 01020304"); + PrintAndLog("sample: hf mf csetuid 01020304 0004 08"); + PrintAndLog("Set UID, ATQA, and SAK for magic Chinese card (only works with such cards)"); + return 0; + } + + PrintAndLog("uid:%s", sprint_hex(uid, 4)); + if (atqaPresent) { + PrintAndLog("--atqa:%s sak:%02x", sprint_hex(atqa, 2), sak[0]); + } + + res = mfCSetUID(uid, (atqaPresent)?atqa:NULL, (atqaPresent)?sak:NULL, oldUid); if (res) { - PrintAndLog("Can't set UID. error=%d", res); + PrintAndLog("Can't set UID. Error=%d", res); return 1; } @@ -1850,6 +2034,69 @@ int CmdHF14AMfCSetUID(const char *Cmd) return 0; } +int CmdHF14AMfCWipe(const char *Cmd) +{ + int res, gen = 0; + int numBlocks = 16 * 4; + bool wipeCard = false; + bool fillCard = false; + + if (strlen(Cmd) < 1 || param_getchar(Cmd, 0) == 'h') { + PrintAndLog("Usage: hf mf cwipe [card size] [w] [f]"); + PrintAndLog("sample: hf mf cwipe 1 w f"); + PrintAndLog("[card size]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLog("w - Wipe magic Chinese card (only works with gen:1a cards)"); + PrintAndLog("f - Fill the card with default data and keys (works with gen:1a and gen:1b cards only)"); + return 0; + } + + gen = mfCIdentify(); + if ((gen != 1) && (gen != 2)) + return 1; + + numBlocks = ParamCardSizeBlocks(param_getchar(Cmd, 0)); + + char cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00){ + switch(param_getchar(Cmd, cmdp)) { + case 'w': + case 'W': + wipeCard = 1; + break; + case 'f': + case 'F': + fillCard = 1; + break; + default: + break; + } + cmdp++; + } + + if (!wipeCard && !fillCard) + wipeCard = true; + + PrintAndLog("--blocks count:%2d wipe:%c fill:%c", numBlocks, (wipeCard)?'y':'n', (fillCard)?'y':'n'); + + if (gen == 2) { + /* generation 1b magic card */ + if (wipeCard) { + PrintAndLog("WARNING: can't wipe magic card 1b generation"); + } + res = mfCWipe(numBlocks, true, false, fillCard); + } else { + /* generation 1a magic card by default */ + res = mfCWipe(numBlocks, false, wipeCard, fillCard); + } + + if (res) { + PrintAndLog("Can't wipe. error=%d", res); + return 1; + } + PrintAndLog("OK"); + return 0; +} + int CmdHF14AMfCSetBlk(const char *Cmd) { uint8_t memBlock[16] = {0x00}; @@ -1866,6 +2113,8 @@ int CmdHF14AMfCSetBlk(const char *Cmd) } gen = mfCIdentify(); + if ((gen != 1) && (gen != 2)) + return 1; blockNo = param_get8(Cmd, 0); @@ -1944,7 +2193,7 @@ int CmdHF14AMfCLoad(const char *Cmd) } return 0; } else { - param_getstr(Cmd, 0, filename); + param_getstr(Cmd, 0, filename, sizeof(filename)); len = strlen(filename); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; @@ -2113,13 +2362,13 @@ int CmdHF14AMfCSave(const char *Cmd) { if (param_getchar(Cmd, 0) == 'h') { PrintAndLog("It saves `magic Chinese` card dump into the file `filename.eml` or `cardID.eml`"); PrintAndLog("or into emulator memory (option `e`). 4K card: (option `4`)"); - PrintAndLog("Usage: hf mf esave [file name w/o `.eml`][e][4]"); - PrintAndLog("Sample: hf mf esave "); - PrintAndLog(" hf mf esave filename"); - PrintAndLog(" hf mf esave e"); - PrintAndLog(" hf mf esave 4"); - PrintAndLog(" hf mf esave filename 4"); - PrintAndLog(" hf mf esave e 4"); + PrintAndLog("Usage: hf mf csave [file name w/o `.eml`][e][4]"); + PrintAndLog("Sample: hf mf csave "); + PrintAndLog(" hf mf csave filename"); + PrintAndLog(" hf mf csave e"); + PrintAndLog(" hf mf csave 4"); + PrintAndLog(" hf mf csave filename 4"); + PrintAndLog(" hf mf csave e 4"); return 0; } @@ -2155,7 +2404,7 @@ int CmdHF14AMfCSave(const char *Cmd) { } return 0; } else { - param_getstr(Cmd, 0, filename); + param_getstr(Cmd, 0, filename, sizeof(filename)); len = strlen(filename); if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; @@ -2228,6 +2477,7 @@ int CmdHF14AMfSniff(const char *Cmd){ //var int res = 0; int len = 0; + int parlen = 0; int blockLen = 0; int pckNum = 0; int num = 0; @@ -2239,6 +2489,7 @@ int CmdHF14AMfSniff(const char *Cmd){ uint8_t *buf = NULL; uint16_t bufsize = 0; uint8_t *bufPtr = NULL; + uint8_t parity[16]; char ctmp = param_getchar(Cmd, 0); if ( ctmp == 'h' || ctmp == 'H' ) { @@ -2282,14 +2533,13 @@ int CmdHF14AMfSniff(const char *Cmd){ } UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK,&resp,2000)) { + if (WaitForResponseTimeoutW(CMD_ACK, &resp, 2000, false)) { res = resp.arg[0] & 0xff; uint16_t traceLen = resp.arg[1]; len = resp.arg[2]; if (res == 0) { // we are done - free(buf); - return 0; + break; } if (res == 1) { // there is (more) data to be transferred @@ -2331,6 +2581,7 @@ int CmdHF14AMfSniff(const char *Cmd){ } else { isTag = false; } + parlen = (len - 1) / 8 + 1; bufPtr += 2; if ((len == 14) && (bufPtr[0] == 0xff) && (bufPtr[1] == 0xff) && (bufPtr[12] == 0xff) && (bufPtr[13] == 0xff)) { memcpy(uid, bufPtr + 2, 7); @@ -2349,15 +2600,22 @@ int CmdHF14AMfSniff(const char *Cmd){ if (wantDecrypt) mfTraceInit(uid, atqa, sak, wantSaveToEmlFile); } else { - PrintAndLog("%s(%d):%s", isTag ? "TAG":"RDR", num, sprint_hex(bufPtr, len)); + oddparitybuf(bufPtr, len, parity); + PrintAndLog("%s(%d):%s [%s] c[%s]%c", + isTag ? "TAG":"RDR", + num, + sprint_hex(bufPtr, len), + printBitsPar(bufPtr + len, len), + printBitsPar(parity, len), + memcmp(bufPtr + len, parity, len / 8 + 1) ? '!' : ' '); if (wantLogToFile) AddLogHex(logHexFileName, isTag ? "TAG: ":"RDR: ", bufPtr, len); if (wantDecrypt) - mfTraceDecode(bufPtr, len, wantSaveToEmlFile); + mfTraceDecode(bufPtr, len, bufPtr[len], wantSaveToEmlFile); num++; } bufPtr += len; - bufPtr += ((len-1)/8+1); // ignore parity + bufPtr += parlen; // ignore parity } pckNum = 0; } @@ -2365,6 +2623,9 @@ int CmdHF14AMfSniff(const char *Cmd){ } // while (true) free(buf); + + msleep(300); // wait for exiting arm side. + PrintAndLog("Done."); return 0; } @@ -2376,6 +2637,42 @@ int CmdDecryptTraceCmds(const char *Cmd){ return tryDecryptWord(param_get32ex(Cmd,0,0,16),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16),data,len/2); } +int CmdHF14AMfAuth4(const char *cmd) { + uint8_t keyn[20] = {0}; + int keynlen = 0; + uint8_t key[16] = {0}; + int keylen = 0; + + CLIParserInit("hf mf auth4", + "Executes AES authentication command in ISO14443-4", + "Usage:\n\thf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" + "\thf mf auth4 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication\n"); + + void* argtable[] = { + arg_param_begin, + arg_str1(NULL, NULL, "", NULL), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + CLIGetHexWithReturn(1, keyn, &keynlen); + CLIGetHexWithReturn(2, key, &keylen); + CLIParserFree(); + + if (keynlen != 2) { + PrintAndLog("ERROR: must be 2 bytes long instead of: %d", keynlen); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + return MifareAuth4(NULL, keyn, key, true, false, true); +} + static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, @@ -2385,6 +2682,7 @@ static command_t CommandTable[] = {"dump", CmdHF14AMfDump, 0, "Dump MIFARE classic tag to binary file"}, {"restore", CmdHF14AMfRestore, 0, "Restore MIFARE classic binary file to BLANK tag"}, {"wrbl", CmdHF14AMfWrBl, 0, "Write MIFARE classic block"}, + {"auth4", CmdHF14AMfAuth4, 0, "ISO14443-4 AES authentication"}, {"chk", CmdHF14AMfChk, 0, "Test block keys"}, {"mifare", CmdHF14AMifare, 0, "Read parity error messages."}, {"hardnested", CmdHF14AMfNestedHard, 0, "Nested attack for hardened Mifare cards"}, @@ -2398,6 +2696,7 @@ static command_t CommandTable[] = {"esave", CmdHF14AMfESave, 0, "Save to file emul dump"}, {"ecfill", CmdHF14AMfECFill, 0, "Fill simulator memory with help of keys from simulator"}, {"ekeyprn", CmdHF14AMfEKeyPrn, 0, "Print keys from simulator memory"}, + {"cwipe", CmdHF14AMfCWipe, 0, "Wipe magic Chinese card"}, {"csetuid", CmdHF14AMfCSetUID, 0, "Set UID for magic Chinese card"}, {"csetblk", CmdHF14AMfCSetBlk, 0, "Write block - Magic Chinese card"}, {"cgetblk", CmdHF14AMfCGetBlk, 0, "Read block - Magic Chinese card"}, @@ -2410,11 +2709,9 @@ static command_t CommandTable[] = int CmdHFMF(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - CmdsParse(CommandTable, Cmd); - return 0; + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; } int CmdHelp(const char *Cmd) diff --git a/client/cmdhfmf.h b/client/cmdhfmf.h index fc87b228..3bd3e95a 100644 --- a/client/cmdhfmf.h +++ b/client/cmdhfmf.h @@ -11,6 +11,8 @@ #ifndef CMDHFMF_H__ #define CMDHFMF_H__ +#include "mifaredefault.h" + extern int CmdHFMF(const char *Cmd); extern int CmdHF14AMfDbg(const char* cmd); @@ -34,6 +36,7 @@ extern int CmdHF14AMfELoad(const char* cmd); extern int CmdHF14AMfESave(const char* cmd); extern int CmdHF14AMfECFill(const char* cmd); extern int CmdHF14AMfEKeyPrn(const char* cmd); +extern int CmdHF14AMfCWipe(const char* cmd); extern int CmdHF14AMfCSetUID(const char* cmd); extern int CmdHF14AMfCSetBlk(const char* cmd); extern int CmdHF14AMfCGetBlk(const char* cmd); diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index 0153541e..4a379c71 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -25,6 +25,7 @@ #include #include #include "proxmark3.h" +#include "comms.h" #include "cmdmain.h" #include "ui.h" #include "util.h" @@ -32,6 +33,7 @@ #include "crapto1/crapto1.h" #include "parity.h" #include "hardnested/hardnested_bruteforce.h" +#include "hardnested/hardnested_bf_core.h" #include "hardnested/hardnested_bitarray_core.h" #include "zlib.h" @@ -71,27 +73,32 @@ static float brute_force_per_second; static void get_SIMD_instruction_set(char* instruction_set) { -#if defined (__i386__) || defined (__x86_64__) - #if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) - #if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) - if (__builtin_cpu_supports("avx512f")) strcpy(instruction_set, "AVX512F"); - else if (__builtin_cpu_supports("avx2")) strcpy(instruction_set, "AVX2"); - #else - if (__builtin_cpu_supports("avx2")) strcpy(instruction_set, "AVX2"); - #endif - else if (__builtin_cpu_supports("avx")) strcpy(instruction_set, "AVX"); - else if (__builtin_cpu_supports("sse2")) strcpy(instruction_set, "SSE2"); - else if (__builtin_cpu_supports("mmx")) strcpy(instruction_set, "MMX"); - else - #endif -#endif - strcpy(instruction_set, "no"); + switch(GetSIMDInstrAuto()) { + case SIMD_AVX512: + strcpy(instruction_set, "AVX512F"); + break; + case SIMD_AVX2: + strcpy(instruction_set, "AVX2"); + break; + case SIMD_AVX: + strcpy(instruction_set, "AVX"); + break; + case SIMD_SSE2: + strcpy(instruction_set, "SSE2"); + break; + case SIMD_MMX: + strcpy(instruction_set, "MMX"); + break; + default: + strcpy(instruction_set, "no"); + break; + } } static void print_progress_header(void) { char progress_text[80]; - char instr_set[12] = ""; + char instr_set[12] = {0}; get_SIMD_instruction_set(instr_set); sprintf(progress_text, "Start using %d threads and %s SIMD core", num_CPUs(), instr_set); PrintAndLog("\n\n"); @@ -144,12 +151,6 @@ static inline void set_bit24(uint32_t *bitarray, uint32_t index) } -static inline void clear_bit24(uint32_t *bitarray, uint32_t index) -{ - bitarray[index>>5] &= ~(0x80000000>>(index&0x0000001f)); -} - - static inline uint32_t test_bit24(uint32_t *bitarray, uint32_t index) { return bitarray[index>>5] & (0x80000000>>(index&0x0000001f)); @@ -190,40 +191,6 @@ static inline uint32_t next_state(uint32_t *bitarray, uint32_t state) } -static inline uint32_t next_not_state(uint32_t *bitarray, uint32_t state) -{ - if (++state == 1<<24) return 1<<24; - uint32_t index = state >> 5; - uint_fast8_t bit = state & 0x1f; - uint32_t line = bitarray[index] << bit; - while (bit <= 0x1f) { - if ((line & 0x80000000) == 0) return state; - state++; - bit++; - line <<= 1; - } - index++; - while (bitarray[index] == 0xffffffff && state < 1<<24) { - index++; - state += 0x20; - } - if (state >= 1<<24) return 1<<24; -#if defined __GNUC__ - return state + __builtin_clz(~bitarray[index]); -#else - bit = 0x00; - line = bitarray[index]; - while (bit <= 0x1f) { - if ((line & 0x80000000) == 0) return state; - state++; - bit++; - line <<= 1; - } - return 1<<24; -#endif -} - - #define BITFLIP_2ND_BYTE 0x0200 @@ -309,7 +276,6 @@ static void init_bitflip_bitarrays(void) if (bytesread != filesize) { printf("File read error with %s. Aborting...\n", state_file_name); fclose(statesfile); - inflateEnd(&compressed_stream); exit(5); } fclose(statesfile); @@ -1192,7 +1158,13 @@ static bool timeout(void) } -static void *check_for_BitFlipProperties_thread(void *args) +static void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*check_for_BitFlipProperties_thread(void *args) { uint8_t first_byte = ((uint8_t *)args)[0]; uint8_t last_byte = ((uint8_t *)args)[1]; @@ -1940,7 +1912,13 @@ static void init_book_of_work(void) } -static void *generate_candidates_worker_thread(void *args) +static void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*generate_candidates_worker_thread(void *args) { uint16_t *sum_args = (uint16_t *)args; uint16_t sum_a0 = sums[sum_args[0]]; @@ -2528,6 +2506,10 @@ static void set_test_state(uint8_t byte) int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow, int tests) { char progress_text[80]; + + char instr_set[12] = {0}; + get_SIMD_instruction_set(instr_set); + PrintAndLog("Using %s SIMD core.", instr_set); srand((unsigned) time(NULL)); brute_force_per_second = brute_force_benchmark(); diff --git a/client/cmdhfmfp.c b/client/cmdhfmfp.c new file mode 100644 index 00000000..1ff24eb1 --- /dev/null +++ b/client/cmdhfmfp.c @@ -0,0 +1,759 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 Merlok +// Copyright (C) 2018 drHatson +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency MIFARE Plus commands +//----------------------------------------------------------------------------- + +#include "cmdhfmfp.h" + +#include +#include +#include +#include +#include +#include "comms.h" +#include "cmdmain.h" +#include "util.h" +#include "ui.h" +#include "cmdhf14a.h" +#include "mifare.h" +#include "mifare4.h" +#include "cliparser/cliparser.h" +#include "polarssl/libpcrypto.h" + +static const uint8_t DefaultKey[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +typedef struct { + uint8_t Code; + const char *Description; +} PlusErrorsElm; + +static const PlusErrorsElm PlusErrors[] = { + {0xFF, ""}, + {0x00, "Transfer cannot be granted within the current authentication."}, + {0x06, "Access Conditions not fulfilled. Block does not exist, block is not a value block."}, + {0x07, "Too many read or write commands in the session or in the transaction."}, + {0x08, "Invalid MAC in command or response"}, + {0x09, "Block Number is not valid"}, + {0x0a, "Invalid block number, not existing block number"}, + {0x0b, "The current command code not available at the current card state."}, + {0x0c, "Length error"}, + {0x0f, "General Manipulation Error. Failure in the operation of the PICC (cannot write to the data block), etc."}, + {0x90, "OK"}, +}; +int PlusErrorsLen = sizeof(PlusErrors) / sizeof(PlusErrorsElm); + +const char * GetErrorDescription(uint8_t errorCode) { + for(int i = 0; i < PlusErrorsLen; i++) + if (errorCode == PlusErrors[i].Code) + return PlusErrors[i].Description; + + return PlusErrors[0].Description; +} + +static int CmdHelp(const char *Cmd); + +static bool VerboseMode = false; +void SetVerboseMode(bool verbose) { + VerboseMode = verbose; +} + +int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + if(VerboseMode) + PrintAndLog(">>> %s", sprint_hex(datain, datainlen)); + + int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); + + if(VerboseMode) + PrintAndLog("<<< %s", sprint_hex(dataout, *dataoutlen)); + + return res; +} + +int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + uint8_t rcmd[3 + 16] = {0xa8, keyNum[1], keyNum[0], 0x00}; + memmove(&rcmd[3], key, 16); + + return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); +} + +int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + uint8_t rcmd[1] = {0xaa}; + + return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); +} + +int MFPReadBlock(mf4Session *session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { + uint8_t rcmd[4 + 8] = {(plain?(0x37):(0x33)), blockNum, 0x00, blockCount}; + if (!plain && session) + CalculateMAC(session, mtypReadCmd, blockNum, blockCount, rcmd, 4, &rcmd[4], VerboseMode); + + int res = intExchangeRAW14aPlus(rcmd, plain?4:sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); + if(res) + return res; + + if (session) + session->R_Ctr++; + + if(session && mac && *dataoutlen > 11) + CalculateMAC(session, mtypReadResp, blockNum, blockCount, dataout, *dataoutlen - 8 - 2, mac, VerboseMode); + + return 0; +} + +int MFPWriteBlock(mf4Session *session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { + uint8_t rcmd[1 + 2 + 16 + 8] = {0xA3, blockNum, 0x00}; + memmove(&rcmd[3], data, 16); + if (session) + CalculateMAC(session, mtypWriteCmd, blockNum, 1, rcmd, 19, &rcmd[19], VerboseMode); + + int res = intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); + if(res) + return res; + + if (session) + session->W_Ctr++; + + if(session && mac && *dataoutlen > 3) + CalculateMAC(session, mtypWriteResp, blockNum, 1, dataout, *dataoutlen, mac, VerboseMode); + + return 0; +} + +int CmdHFMFPInfo(const char *cmd) { + + if (cmd && strlen(cmd) > 0) + PrintAndLog("WARNING: command don't have any parameters.\n"); + + // info about 14a part + CmdHF14AInfo(""); + + // Mifare Plus info + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + if (select_status == 1 || select_status == 2) { + PrintAndLog("----------------------------------------------"); + PrintAndLog("Mifare Plus info:"); + + // MIFARE Type Identification Procedure + // https://www.nxp.com/docs/en/application-note/AN10833.pdf + uint16_t ATQA = card.atqa[0] + (card.atqa[1] << 8); + if (ATQA == 0x0004) PrintAndLog("ATQA: Mifare Plus 2k 4bUID"); + if (ATQA == 0x0002) PrintAndLog("ATQA: Mifare Plus 4k 4bUID"); + if (ATQA == 0x0044) PrintAndLog("ATQA: Mifare Plus 2k 7bUID"); + if (ATQA == 0x0042) PrintAndLog("ATQA: Mifare Plus 4k 7bUID"); + + uint8_t SLmode = 0xff; + if (card.sak == 0x08) { + PrintAndLog("SAK: Mifare Plus 2k 7bUID"); + if (select_status == 2) SLmode = 1; + } + if (card.sak == 0x18) { + PrintAndLog("SAK: Mifare Plus 4k 7bUID"); + if (select_status == 2) SLmode = 1; + } + if (card.sak == 0x10) { + PrintAndLog("SAK: Mifare Plus 2k"); + if (select_status == 2) SLmode = 2; + } + if (card.sak == 0x11) { + PrintAndLog("SAK: Mifare Plus 4k"); + if (select_status == 2) SLmode = 2; + } + if (card.sak == 0x20) { + PrintAndLog("SAK: Mifare Plus SL0/SL3 or Mifare desfire"); + if (card.ats_len > 0) { + SLmode = 3; + + // check SL0 + uint8_t data[250] = {0}; + int datalen = 0; + // https://github.com/Proxmark/proxmark3/blob/master/client/scripts/mifarePlus.lua#L161 + uint8_t cmd[3 + 16] = {0xa8, 0x90, 0x90, 0x00}; + int res = ExchangeRAW14a(cmd, sizeof(cmd), false, false, data, sizeof(data), &datalen); + if (!res && datalen > 1 && data[0] == 0x09) { + SLmode = 0; + } + } + } + + if (SLmode != 0xff) + PrintAndLog("Mifare Plus SL mode: SL%d", SLmode); + else + PrintAndLog("Mifare Plus SL mode: unknown("); + } else { + PrintAndLog("Mifare Plus info not available."); + } + + DropField(); + + return 0; +} + +int CmdHFMFPWritePerso(const char *cmd) { + uint8_t keyNum[64] = {0}; + int keyNumLen = 0; + uint8_t key[64] = {0}; + int keyLen = 0; + + CLIParserInit("hf mfp wrp", + "Executes Write Perso command. Can be used in SL0 mode only.", + "Usage:\n\thf mfp wrp 4000 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n" + "\thf mfp wrp 4000 -> write default key(0xff..0xff) to key number 4000"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_str1(NULL, NULL, "", NULL), + arg_strx0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + CLIGetHexWithReturn(2, keyNum, &keyNumLen); + CLIGetHexWithReturn(3, key, &keyLen); + CLIParserFree(); + + SetVerboseMode(verbose); + + if (!keyLen) { + memmove(key, DefaultKey, 16); + keyLen = 16; + } + + if (keyNumLen != 2) { + PrintAndLog("Key number length must be 2 bytes instead of: %d", keyNumLen); + return 1; + } + if (keyLen != 16) { + PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen); + return 1; + } + + uint8_t data[250] = {0}; + int datalen = 0; + + int res = MFPWritePerso(keyNum, key, true, false, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("Exchange error: %d", res); + return res; + } + + if (datalen != 3) { + PrintAndLog("Command must return 3 bytes instead of: %d", datalen); + return 1; + } + + if (data[0] != 0x90) { + PrintAndLog("Command error: %02x %s", data[0], GetErrorDescription(data[0])); + return 1; + } + PrintAndLog("Write OK."); + + return 0; +} + +uint16_t CardAddresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001}; + +int CmdHFMFPInitPerso(const char *cmd) { + int res; + uint8_t key[256] = {0}; + int keyLen = 0; + uint8_t keyNum[2] = {0}; + uint8_t data[250] = {0}; + int datalen = 0; + + CLIParserInit("hf mfp initp", + "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.", + "Usage:\n\thf mfp initp 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n" + "\thf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange"); + + void* argtable[] = { + arg_param_begin, + arg_litn("vV", "verbose", 0, 2, "show internal data."), + arg_strx0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + bool verbose2 = arg_get_lit(1) > 1; + CLIGetHexWithReturn(2, key, &keyLen); + CLIParserFree(); + + if (keyLen && keyLen != 16) { + PrintAndLog("Key length must be 16 bytes instead of: %d", keyLen); + return 1; + } + + if (!keyLen) + memmove(key, DefaultKey, 16); + + SetVerboseMode(verbose2); + for (uint16_t sn = 0x4000; sn < 0x4050; sn++) { + keyNum[0] = sn >> 8; + keyNum[1] = sn & 0xff; + res = MFPWritePerso(keyNum, key, (sn == 0x4000), true, data, sizeof(data), &datalen); + if (!res && (datalen == 3) && data[0] == 0x09) { + PrintAndLog("2k card detected."); + break; + } + if (res || (datalen != 3) || data[0] != 0x90) { + PrintAndLog("Write error on address %04x", sn); + break; + } + } + + SetVerboseMode(verbose); + for (int i = 0; i < sizeof(CardAddresses) / 2; i++) { + keyNum[0] = CardAddresses[i] >> 8; + keyNum[1] = CardAddresses[i] & 0xff; + res = MFPWritePerso(keyNum, key, false, true, data, sizeof(data), &datalen); + if (!res && (datalen == 3) && data[0] == 0x09) { + PrintAndLog("Skipped[%04x]...", CardAddresses[i]); + } else { + if (res || (datalen != 3) || data[0] != 0x90) { + PrintAndLog("Write error on address %04x", CardAddresses[i]); + break; + } + } + } + + DropField(); + + if (res) + return res; + + PrintAndLog("Done."); + + return 0; +} + +int CmdHFMFPCommitPerso(const char *cmd) { + CLIParserInit("hf mfp commitp", + "Executes Commit Perso command. Can be used in SL0 mode only.", + "Usage:\n\thf mfp commitp -> \n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_int0(NULL, NULL, "SL mode", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + CLIParserFree(); + + SetVerboseMode(verbose); + + uint8_t data[250] = {0}; + int datalen = 0; + + int res = MFPCommitPerso(true, false, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("Exchange error: %d", res); + return res; + } + + if (datalen != 3) { + PrintAndLog("Command must return 3 bytes instead of: %d", datalen); + return 1; + } + + if (data[0] != 0x90) { + PrintAndLog("Command error: %02x %s", data[0], GetErrorDescription(data[0])); + return 1; + } + PrintAndLog("Switch level OK."); + + return 0; +} + +int CmdHFMFPAuth(const char *cmd) { + uint8_t keyn[250] = {0}; + int keynlen = 0; + uint8_t key[250] = {0}; + int keylen = 0; + + CLIParserInit("hf mfp auth", + "Executes AES authentication command for Mifare Plus card", + "Usage:\n\thf mfp auth 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" + "\thf mfp auth 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_str1(NULL, NULL, "", NULL), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + CLIGetHexWithReturn(2, keyn, &keynlen); + CLIGetHexWithReturn(3, key, &keylen); + CLIParserFree(); + + if (keynlen != 2) { + PrintAndLog("ERROR: must be 2 bytes long instead of: %d", keynlen); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + return MifareAuth4(NULL, keyn, key, true, false, verbose); +} + +int CmdHFMFPRdbl(const char *cmd) { + uint8_t keyn[2] = {0}; + uint8_t key[250] = {0}; + int keylen = 0; + + CLIParserInit("hf mfp rdbl", + "Reads several blocks from Mifare Plus card.", + "Usage:\n\thf mfp rdbl 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n" + "\thf mfp rdbl 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_int0("nN", "count", "blocks count (by default 1).", NULL), + arg_lit0("bB", "keyb", "use key B (by default keyA)."), + arg_lit0("pP", "plain", "plain communication mode between reader and card."), + arg_int1(NULL, NULL, "", NULL), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool verbose = arg_get_lit(1); + int blocksCount = arg_get_int_def(2, 1); + bool keyB = arg_get_lit(3); + int plain = arg_get_lit(4); + uint32_t blockn = arg_get_int(5); + CLIGetHexWithReturn(6, key, &keylen); + CLIParserFree(); + + SetVerboseMode(verbose); + + if (!keylen) { + memmove(key, DefaultKey, 16); + keylen = 16; + } + + if (blockn > 255) { + PrintAndLog("ERROR: must be in range [0..255] instead of: %d", blockn); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + // 3 blocks - wo iso14443-4 chaining + if (blocksCount > 3) { + PrintAndLog("ERROR: blocks count must be less than 3 instead of: %d", blocksCount); + return 1; + } + + if (blocksCount > 1 && mfIsSectorTrailer(blockn)) { + PrintAndLog("WARNING: trailer!"); + } + + uint8_t sectorNum = mfSectorNum(blockn & 0xff); + uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); + keyn[0] = uKeyNum >> 8; + keyn[1] = uKeyNum & 0xff; + if (verbose) + PrintAndLog("--block:%d sector[%d]:%02x key:%04x", blockn, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + + mf4Session session; + int res = MifareAuth4(&session, keyn, key, true, true, verbose); + if (res) { + PrintAndLog("Authentication error: %d", res); + return res; + } + + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + res = MFPReadBlock(&session, plain, blockn & 0xff, blocksCount, false, false, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLog("Read error: %d", res); + return res; + } + + if (datalen && data[0] != 0x90) { + PrintAndLog("Card read error: %02x %s", data[0], GetErrorDescription(data[0])); + return 6; + } + + if (datalen != 1 + blocksCount * 16 + 8 + 2) { + PrintAndLog("Error return length:%d", datalen); + return 5; + } + + int indx = blockn; + for(int i = 0; i < blocksCount; i++) { + PrintAndLog("data[%03d]: %s", indx, sprint_hex(&data[1 + i * 16], 16)); + indx++; + if (mfIsSectorTrailer(indx) && i != blocksCount - 1){ + PrintAndLog("data[%03d]: ------------------- trailer -------------------", indx); + indx++; + } + } + + if (memcmp(&data[blocksCount * 16 + 1], mac, 8)) { + PrintAndLog("WARNING: mac not equal..."); + PrintAndLog("MAC card: %s", sprint_hex(&data[blocksCount * 16 + 1], 8)); + PrintAndLog("MAC reader: %s", sprint_hex(mac, 8)); + } else { + if(verbose) + PrintAndLog("MAC: %s", sprint_hex(&data[blocksCount * 16 + 1], 8)); + } + + return 0; +} + +int CmdHFMFPRdsc(const char *cmd) { + uint8_t keyn[2] = {0}; + uint8_t key[250] = {0}; + int keylen = 0; + + CLIParserInit("hf mfp rdsc", + "Reads one sector from Mifare Plus card.", + "Usage:\n\thf mfp rdsc 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n" + "\thf mfp rdsc 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_lit0("bB", "keyb", "use key B (by default keyA)."), + arg_lit0("pP", "plain", "plain communication mode between reader and card."), + arg_int1(NULL, NULL, "", NULL), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool verbose = arg_get_lit(1); + bool keyB = arg_get_lit(2); + bool plain = arg_get_lit(3); + uint32_t sectorNum = arg_get_int(4); + CLIGetHexWithReturn(5, key, &keylen); + CLIParserFree(); + + SetVerboseMode(verbose); + + if (!keylen) { + memmove(key, DefaultKey, 16); + keylen = 16; + } + + if (sectorNum > 39) { + PrintAndLog("ERROR: must be in range [0..39] instead of: %d", sectorNum); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); + keyn[0] = uKeyNum >> 8; + keyn[1] = uKeyNum & 0xff; + if (verbose) + PrintAndLog("--sector[%d]:%02x key:%04x", mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + + mf4Session session; + int res = MifareAuth4(&session, keyn, key, true, true, verbose); + if (res) { + PrintAndLog("Authentication error: %d", res); + return res; + } + + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + for(int n = mfFirstBlockOfSector(sectorNum); n < mfFirstBlockOfSector(sectorNum) + mfNumBlocksPerSector(sectorNum); n++) { + res = MFPReadBlock(&session, plain, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLog("Read error: %d", res); + DropField(); + return res; + } + + if (datalen && data[0] != 0x90) { + PrintAndLog("Card read error: %02x %s", data[0], GetErrorDescription(data[0])); + DropField(); + return 6; + } + if (datalen != 1 + 16 + 8 + 2) { + PrintAndLog("Error return length:%d", datalen); + DropField(); + return 5; + } + + PrintAndLog("data[%03d]: %s", n, sprint_hex(&data[1], 16)); + + if (memcmp(&data[1 + 16], mac, 8)) { + PrintAndLog("WARNING: mac on block %d not equal...", n); + PrintAndLog("MAC card: %s", sprint_hex(&data[1 + 16], 8)); + PrintAndLog("MAC reader: %s", sprint_hex(mac, 8)); + } else { + if(verbose) + PrintAndLog("MAC: %s", sprint_hex(&data[1 + 16], 8)); + } + } + DropField(); + + return 0; +} + +int CmdHFMFPWrbl(const char *cmd) { + uint8_t keyn[2] = {0}; + uint8_t key[250] = {0}; + int keylen = 0; + uint8_t datain[250] = {0}; + int datainlen = 0; + + CLIParserInit("hf mfp wrbl", + "Writes one block to Mifare Plus card.", + "Usage:\n\thf mfp wrbl 1 ff0000000000000000000000000000ff 000102030405060708090a0b0c0d0e0f -> writes block 1 data\n" + "\thf mfp wrbl 2 ff0000000000000000000000000000ff -v -> writes block 2 data with default key 0xFF..0xFF and some additional data\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show internal data."), + arg_lit0("bB", "keyb", "use key B (by default keyA)."), + arg_int1(NULL, NULL, "", NULL), + arg_str1(NULL, NULL, "", NULL), + arg_str0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool verbose = arg_get_lit(1); + bool keyB = arg_get_lit(2); + uint32_t blockNum = arg_get_int(3); + CLIGetHexWithReturn(4, datain, &datainlen); + CLIGetHexWithReturn(5, key, &keylen); + CLIParserFree(); + + SetVerboseMode(verbose); + + if (!keylen) { + memmove(key, DefaultKey, 16); + keylen = 16; + } + + if (blockNum > 39) { + PrintAndLog("ERROR: must be in range [0..255] instead of: %d", blockNum); + return 1; + } + + if (keylen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", keylen); + return 1; + } + + if (datainlen != 16) { + PrintAndLog("ERROR: must be 16 bytes long instead of: %d", datainlen); + return 1; + } + + uint8_t sectorNum = mfSectorNum(blockNum & 0xff); + uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); + keyn[0] = uKeyNum >> 8; + keyn[1] = uKeyNum & 0xff; + if (verbose) + PrintAndLog("--block:%d sector[%d]:%02x key:%04x", blockNum & 0xff, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + + mf4Session session; + int res = MifareAuth4(&session, keyn, key, true, true, verbose); + if (res) { + PrintAndLog("Authentication error: %d", res); + return res; + } + + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + res = MFPWriteBlock(&session, blockNum & 0xff, datain, false, false, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLog("Write error: %d", res); + DropField(); + return res; + } + + if (datalen != 3 && (datalen != 3 + 8)) { + PrintAndLog("Error return length:%d", datalen); + DropField(); + return 5; + } + + if (datalen && data[0] != 0x90) { + PrintAndLog("Card write error: %02x %s", data[0], GetErrorDescription(data[0])); + DropField(); + return 6; + } + + if (memcmp(&data[1], mac, 8)) { + PrintAndLog("WARNING: mac not equal..."); + PrintAndLog("MAC card: %s", sprint_hex(&data[1], 8)); + PrintAndLog("MAC reader: %s", sprint_hex(mac, 8)); + } else { + if(verbose) + PrintAndLog("MAC: %s", sprint_hex(&data[1], 8)); + } + + DropField(); + PrintAndLog("Write OK."); + return 0; +} + +static command_t CommandTable[] = +{ + {"help", CmdHelp, 1, "This help"}, + {"info", CmdHFMFPInfo, 0, "Info about Mifare Plus tag"}, + {"wrp", CmdHFMFPWritePerso, 0, "Write Perso command"}, + {"initp", CmdHFMFPInitPerso, 0, "Fills all the card's keys"}, + {"commitp", CmdHFMFPCommitPerso, 0, "Move card to SL1 or SL3 mode"}, + {"auth", CmdHFMFPAuth, 0, "Authentication"}, + {"rdbl", CmdHFMFPRdbl, 0, "Read blocks"}, + {"rdsc", CmdHFMFPRdsc, 0, "Read sectors"}, + {"wrbl", CmdHFMFPWrbl, 0, "Write blocks"}, + {NULL, NULL, 0, NULL} +}; + +int CmdHFMFP(const char *Cmd) { + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdhfmfp.h b/client/cmdhfmfp.h new file mode 100644 index 00000000..b1ac7c34 --- /dev/null +++ b/client/cmdhfmfp.h @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency MIFARE Plus commands +//----------------------------------------------------------------------------- +#ifndef CMDHFMFP_H__ +#define CMDHFMFP_H__ + +#include "mifaredefault.h" + +extern int CmdHFMFP(const char *Cmd); + + +#endif \ No newline at end of file diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index ca4544f3..1077ec99 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -12,7 +12,7 @@ #include #include -#include "proxmark3.h" +#include "comms.h" #include "usb_cmd.h" #include "cmdmain.h" #include "ui.h" @@ -22,7 +22,6 @@ #include "mifare.h" #include "util.h" #include "protocols.h" -#include "data.h" #define MAX_UL_BLOCKS 0x0f #define MAX_ULC_BLOCKS 0x2b @@ -108,13 +107,7 @@ char *getUlev1CardSizeStr( uint8_t fsize ){ } static void ul_switch_on_field(void) { - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); -} - -void ul_switch_off_field(void) { - UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 0, 0}}; clearCommandBuffer(); SendCommand(&c); } @@ -159,7 +152,7 @@ static int ul_select( iso14a_card_select_t *card ){ ans = WaitForResponseTimeout(CMD_ACK, &resp, 1500); if (!ans || resp.arg[0] < 1) { PrintAndLog("iso14443a card select failed"); - ul_switch_off_field(); + DropField(); return 0; } @@ -232,7 +225,7 @@ static int ul_auth_select( iso14a_card_select_t *card, TagTypeUL_t tagtype, bool if (hasAuthKey) { if (ulev1_requestAuthentication(authenticationkey, pack, packSize) < 1) { - ul_switch_off_field(); + DropField(); PrintAndLog("Error: Authentication Failed UL-EV1/NTAG"); return 0; } @@ -556,7 +549,7 @@ static int ulc_magic_test(){ } else { returnValue = UL; } - ul_switch_off_field(); + DropField(); return returnValue; } */ @@ -569,7 +562,7 @@ static int ul_magic_test(){ if ( !ul_select(&card) ) return UL_ERROR; int status = ul_comp_write(0, NULL, 0); - ul_switch_off_field(); + DropField(); if ( status == 0 ) return MAGIC; return 0; @@ -588,14 +581,14 @@ uint32_t GetHF14AMfU_Type(void){ // Ultralight - ATQA / SAK if ( card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00 ) { PrintAndLog("Tag is not Ultralight | NTAG | MY-D [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); - ul_switch_off_field(); + DropField(); return UL_ERROR; } if ( card.uid[0] != 0x05) { len = ulev1_getVersion(version, sizeof(version)); - ul_switch_off_field(); + DropField(); switch (len) { case 0x0A: { @@ -635,7 +628,7 @@ uint32_t GetHF14AMfU_Type(void){ // do UL_C check first... uint8_t nonce[11] = {0x00}; status = ulc_requestAuthentication(nonce, sizeof(nonce)); - ul_switch_off_field(); + DropField(); if (status > 1) { tagtype = UL_C; } else { @@ -656,15 +649,15 @@ uint32_t GetHF14AMfU_Type(void){ tagtype = UNKNOWN; } } - ul_switch_off_field(); + DropField(); } } if (tagtype & UL) { tagtype = ul_fudan_check(); - ul_switch_off_field(); + DropField(); } } else { - ul_switch_off_field(); + DropField(); // Infinition MY-D tests Exam high nibble uint8_t nib = (card.uid[1] & 0xf0) >> 4; switch ( nib ){ @@ -709,7 +702,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ return usage_hf_mfu_info(); case 'k': case 'K': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 32 || dataLen == 8) { //ul-c or ev1/ntag key length errors = param_gethex(tempStr, 0, authenticationkey, dataLen); dataLen /= 2; // handled as bytes from now on @@ -751,7 +744,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ // read pages 0,1,2,3 (should read 4pages) status = ul_read(0, data, sizeof(data)); if ( status == -1 ) { - ul_switch_off_field(); + DropField(); PrintAndLog("Error: tag didn't answer to READ"); return status; } else if (status == 16) { @@ -769,7 +762,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ status = ul_read(0x28, ulc_conf, sizeof(ulc_conf)); if ( status == -1 ){ PrintAndLog("Error: tag didn't answer to READ UL-C"); - ul_switch_off_field(); + DropField(); return status; } if (status == 16) ulc_print_configuration(ulc_conf); @@ -780,14 +773,14 @@ int CmdHF14AMfUInfo(const char *Cmd){ uint8_t ulc_deskey[16] = {0x00}; status = ul_read(0x2C, ulc_deskey, sizeof(ulc_deskey)); if ( status == -1 ) { - ul_switch_off_field(); + DropField(); PrintAndLog("Error: tag didn't answer to READ magic"); return status; } if (status == 16) ulc_print_3deskey(ulc_deskey); } else { - ul_switch_off_field(); + DropField(); // if we called info with key, just return if ( hasAuthKey ) return 1; @@ -822,7 +815,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ status = ulev1_readSignature( ulev1_signature, sizeof(ulev1_signature)); if ( status == -1 ) { PrintAndLog("Error: tag didn't answer to READ SIGNATURE"); - ul_switch_off_field(); + DropField(); return status; } if (status == 32) ulev1_print_signature( ulev1_signature, sizeof(ulev1_signature)); @@ -837,7 +830,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ status = ulev1_getVersion(version, sizeof(version)); if ( status == -1 ) { PrintAndLog("Error: tag didn't answer to GETVERSION"); - ul_switch_off_field(); + DropField(); return status; } else if (status == 10) { ulev1_print_version(version); @@ -857,7 +850,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ status = ul_read(startconfigblock, ulev1_conf, sizeof(ulev1_conf)); if ( status == -1 ) { PrintAndLog("Error: tag didn't answer to READ EV1"); - ul_switch_off_field(); + DropField(); return status; } else if (status == 16) { // save AUTHENTICATION LIMITS for later: @@ -887,7 +880,7 @@ int CmdHF14AMfUInfo(const char *Cmd){ } } - ul_switch_off_field(); + DropField(); if (locked) PrintAndLog("\nTag appears to be locked, try using the key to get more info"); PrintAndLog(""); return 1; @@ -1244,7 +1237,7 @@ int CmdHF14AMfUDump(const char *Cmd){ return usage_hf_mfu_dump(); case 'k': case 'K': - dataLen = param_getstr(Cmd, cmdp+1, tempStr); + dataLen = param_getstr(Cmd, cmdp+1, tempStr, sizeof(tempStr)); if (dataLen == 32 || dataLen == 8) { //ul-c or ev1/ntag key length errors = param_gethex(tempStr, 0, authenticationkey, dataLen); dataLen /= 2; @@ -1262,7 +1255,7 @@ int CmdHF14AMfUDump(const char *Cmd){ break; case 'n': case 'N': - fileNlen = param_getstr(Cmd, cmdp+1, filename); + fileNlen = param_getstr(Cmd, cmdp+1, filename, sizeof(filename)); if (!fileNlen) errors = true; if (fileNlen > FILE_PATH_SIZE-5) fileNlen = FILE_PATH_SIZE-5; cmdp += 2; @@ -1331,8 +1324,7 @@ int CmdHF14AMfUDump(const char *Cmd){ PrintAndLog("Data exceeded Buffer size!"); bufferSize = sizeof(data); } - GetFromBigBuf(data, bufferSize, startindex); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(data, bufferSize, startindex, NULL, -1, false); Pages = bufferSize/4; // Load lock bytes. @@ -1842,7 +1834,7 @@ static command_t CommandTable[] = }; int CmdHFMFUltra(const char *Cmd){ - WaitForResponseTimeout(CMD_ACK,NULL,100); + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); CmdsParse(CommandTable, Cmd); return 0; } diff --git a/client/cmdhfmfu.h b/client/cmdhfmfu.h index 6c9e3ea1..43e71d96 100644 --- a/client/cmdhfmfu.h +++ b/client/cmdhfmfu.h @@ -16,7 +16,6 @@ int CmdHF14AMfUInfo(const char *Cmd); uint32_t GetHF14AMfU_Type(void); int ul_print_type(uint32_t tagtype, uint8_t spacer); -void ul_switch_off_field(void); int usage_hf_mfu_dump(void); int usage_hf_mfu_info(void); diff --git a/client/cmdhftopaz.c b/client/cmdhftopaz.c index 39ff1804..6058cabe 100644 --- a/client/cmdhftopaz.c +++ b/client/cmdhftopaz.c @@ -8,17 +8,18 @@ // High frequency Topaz (NFC Type 1) commands //----------------------------------------------------------------------------- +#include "cmdhftopaz.h" + #include #include #include #include #include "cmdmain.h" #include "cmdparser.h" -#include "cmdhftopaz.h" #include "cmdhf14a.h" #include "ui.h" #include "mifare.h" -#include "proxmark3.h" +#include "comms.h" #include "iso14443crc.h" #include "protocols.h" @@ -454,7 +455,7 @@ int CmdHFTopazReader(const char *Cmd) PrintAndLog("HR0 : %02x (%sa Topaz tag (%scapable of carrying a NDEF message), %s memory map)", rid_response[0], (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", - (rid_response[0] & 0x0F) == 0x10 ? "static" : "dynamic"); + (rid_response[0] & 0x0F) == 0x01 ? "static" : "dynamic"); PrintAndLog("HR1 : %02x", rid_response[1]); status = topaz_rall(uid_echo, rall_response); @@ -553,10 +554,7 @@ static command_t CommandTable[] = int CmdHFTopaz(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - // parse + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); CmdsParse(CommandTable, Cmd); return 0; } diff --git a/client/cmdhw.c b/client/cmdhw.c index 8f7243ad..f994e938 100644 --- a/client/cmdhw.c +++ b/client/cmdhw.c @@ -8,17 +8,17 @@ // Hardware commands //----------------------------------------------------------------------------- +#include "cmdhw.h" + #include #include #include #include #include "ui.h" -#include "proxmark3.h" +#include "comms.h" #include "cmdparser.h" -#include "cmdhw.h" #include "cmdmain.h" #include "cmddata.h" -#include "data.h" /* low-level hardware control */ @@ -408,34 +408,23 @@ int CmdVersion(const char *Cmd) clearCommandBuffer(); UsbCommand c = {CMD_VERSION}; - static UsbCommand resp = {0, {0, 0, 0}}; + UsbCommand resp = {0, {0, 0, 0}}; - if (resp.arg[0] == 0 && resp.arg[1] == 0) { // no cached information available - SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - PrintAndLog("Prox/RFID mark3 RFID instrument"); - PrintAndLog((char*)resp.d.asBytes); - lookupChipID(resp.arg[0], resp.arg[1]); - } - } else { - PrintAndLog("[[[ Cached information ]]]\n"); + SendCommand(&c); + if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { PrintAndLog("Prox/RFID mark3 RFID instrument"); PrintAndLog((char*)resp.d.asBytes); lookupChipID(resp.arg[0], resp.arg[1]); - PrintAndLog(""); } return 0; } int CmdStatus(const char *Cmd) { - uint8_t speed_test_buffer[USB_CMD_DATA_SIZE]; - sample_buf = speed_test_buffer; - clearCommandBuffer(); UsbCommand c = {CMD_STATUS}; SendCommand(&c); - if (!WaitForResponseTimeout(CMD_ACK,&c,1900)) { + if (!WaitForResponseTimeout(CMD_ACK, &c, 1900)) { PrintAndLog("Status command failed. USB Speed Test timed out"); } return 0; diff --git a/client/cmdlf.c b/client/cmdlf.c index eb664a11..c09a299c 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -8,21 +8,21 @@ // Low frequency commands //----------------------------------------------------------------------------- +#include "cmdlf.h" + #include #include #include #include #include #include -#include "proxmark3.h" -#include "cmdlf.h" +#include "comms.h" #include "lfdemod.h" // for psk2TOpsk1 #include "util.h" // for parsing cli command utils #include "ui.h" // for show graph controls #include "graph.h" // for graph data #include "cmdparser.h" // for getting cli commands included in cmdmain.h #include "cmdmain.h" // for sending cmds to device -#include "data.h" // for GetFromBigBuf #include "cmddata.h" // for `lf search` #include "cmdlfawid.h" // for awid menu #include "cmdlfem4x.h" // for em4x menu @@ -54,26 +54,24 @@ static int CmdHelp(const char *Cmd); int usage_lf_cmdread(void) { - PrintAndLog("Usage: lf cmdread d z o c [H] "); + PrintAndLog("Usage: lf cmdread d z o c "); PrintAndLog("Options: "); PrintAndLog(" h This help"); - PrintAndLog(" L Low frequency (125 KHz)"); - PrintAndLog(" H High frequency (134 KHz)"); - PrintAndLog(" d delay OFF period"); - PrintAndLog(" z time period ZERO"); - PrintAndLog(" o time period ONE"); + PrintAndLog(" d delay OFF period between bits (0 for bitbang mode)"); + PrintAndLog(" z time period ZERO (antenna off in bitbang mode)"); + PrintAndLog(" o time period ONE (antenna on in bitbang mode)"); PrintAndLog(" c Command bytes"); PrintAndLog(" ************* All periods in microseconds"); + PrintAndLog(" ************* Use lf config to configure options."); PrintAndLog("Examples:"); PrintAndLog(" lf cmdread d 80 z 100 o 200 c 11000"); - PrintAndLog(" lf cmdread d 80 z 100 o 100 c 11000 H"); + PrintAndLog(" lf cmdread d 80 z 100 o 100 c 11000"); return 0; } /* send a command before reading */ int CmdLFCommandRead(const char *Cmd) { - static char dummy[3] = {0x20,0x00,0x00}; UsbCommand c = {CMD_MOD_THEN_ACQUIRE_RAW_ADC_SAMPLES_125K}; bool errors = false; //uint8_t divisor = 95; //125khz @@ -84,16 +82,8 @@ int CmdLFCommandRead(const char *Cmd) { case 'h': return usage_lf_cmdread(); - case 'H': - //divisor = 88; - dummy[1]='h'; - cmdp++; - break; - case 'L': - cmdp++; - break; case 'c': - param_getstr(Cmd, cmdp+1, (char *)&c.d.asBytes); + param_getstr(Cmd, cmdp+1, (char *)&c.d.asBytes, sizeof(c.d.asBytes)); cmdp+=2; break; case 'd': @@ -121,11 +111,13 @@ int CmdLFCommandRead(const char *Cmd) //Validations if(errors) return usage_lf_cmdread(); - // in case they specified 'H' - strcpy((char *)&c.d.asBytes + strlen((char *)c.d.asBytes), dummy); - clearCommandBuffer(); SendCommand(&c); + + WaitForResponse(CMD_ACK,NULL); + getSamples(0, true); + + return 0; } @@ -335,7 +327,7 @@ int CmdLFSetConfig(const char *Cmd) } bool lf_read(bool silent, uint32_t samples) { - if (offline) return false; + if (IsOffline()) return false; UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_125K, {silent,samples,0}}; clearCommandBuffer(); //And ship it to device @@ -491,7 +483,7 @@ int CmdLFfskSim(const char *Cmd) uint8_t fcHigh=0, fcLow=0, clk=0; uint8_t invert=0; bool errors = false; - char hexData[32] = {0x00}; // store entered hex data + char hexData[64] = {0x00}; // store entered hex data uint8_t data[255] = {0x00}; int dataLen = 0; uint8_t cmdp = 0; @@ -522,7 +514,7 @@ int CmdLFfskSim(const char *Cmd) // cmdp++; // break; case 'd': - dataLen = param_getstr(Cmd, cmdp+1, hexData); + dataLen = param_getstr(Cmd, cmdp+1, hexData, sizeof(hexData)); if (dataLen==0) { errors=true; } else { @@ -593,7 +585,7 @@ int CmdLFaskSim(const char *Cmd) uint8_t encoding = 1, separator = 0; uint8_t clk=0, invert=0; bool errors = false; - char hexData[32] = {0x00}; + char hexData[64] = {0x00}; uint8_t data[255]= {0x00}; // store entered hex data int dataLen = 0; uint8_t cmdp = 0; @@ -628,7 +620,7 @@ int CmdLFaskSim(const char *Cmd) cmdp++; break; case 'd': - dataLen = param_getstr(Cmd, cmdp+1, hexData); + dataLen = param_getstr(Cmd, cmdp+1, hexData, sizeof(hexData)); if (dataLen==0) { errors=true; } else { @@ -687,7 +679,7 @@ int CmdLFpskSim(const char *Cmd) uint8_t carrier=0, clk=0; uint8_t invert=0; bool errors = false; - char hexData[32] = {0x00}; // store entered hex data + char hexData[64] = {0x00}; // store entered hex data uint8_t data[255] = {0x00}; int dataLen = 0; uint8_t cmdp = 0; @@ -723,7 +715,7 @@ int CmdLFpskSim(const char *Cmd) cmdp++; break; case 'd': - dataLen = param_getstr(Cmd, cmdp+1, hexData); + dataLen = param_getstr(Cmd, cmdp+1, hexData, sizeof(hexData)); if (dataLen==0) { errors=true; } else { @@ -878,7 +870,7 @@ int CmdVchDemod(const char *Cmd) int CheckChipType(char cmdp) { uint32_t wordData = 0; - if (offline || cmdp == '1') return 0; + if (IsOffline() || cmdp == '1') return 0; save_restoreGB(GRAPH_SAVE); save_restoreDB(GRAPH_SAVE); @@ -923,7 +915,7 @@ int CmdLFfind(const char *Cmd) return 0; } - if (!offline && (cmdp != '1')) { + if (!IsOffline() && (cmdp != '1')) { lf_read(true, 30000); } else if (GraphTraceLen < minLength) { PrintAndLog("Data in Graphbuffer was too small."); @@ -939,13 +931,13 @@ int CmdLFfind(const char *Cmd) // only run if graphbuffer is just noise as it should be for hitag/cotag if (graphJustNoise(GraphBuffer, testLen)) { // only run these tests if we are in online mode - if (!offline && (cmdp != '1')) { + if (!IsOffline() && (cmdp != '1')) { // test for em4x05 in reader talk first mode. if (EM4x05Block0Test(&wordData)) { PrintAndLog("\nValid EM4x05/EM4x69 Chip Found\nUse lf em 4x05readword/dump commands to read\n"); return 1; } - ans=CmdLFHitagReader("26"); + ans=CmdLFHitagReader("26"); // 26 = RHT2F_UID_ONLY if (ans==0) { return 1; } @@ -955,6 +947,7 @@ int CmdLFfind(const char *Cmd) return 1; } } + PrintAndLog("\nNo Data Found! - maybe not an LF tag?\n"); return 0; } diff --git a/client/cmdlfawid.c b/client/cmdlfawid.c index 141ba172..cde94555 100644 --- a/client/cmdlfawid.c +++ b/client/cmdlfawid.c @@ -11,10 +11,11 @@ // FSK2a, RF/50, 96 bits (complete) //----------------------------------------------------------------------------- +#include "cmdlfawid.h" + #include #include // sscanf -#include "proxmark3.h" // Definitions, USB controls, etc -#include "cmdlfawid.h" +#include "comms.h" // Definitions, USB controls, etc #include "ui.h" // PrintAndLog #include "cmdparser.h" // CmdsParse, CmdsHelp #include "lfdemod.h" // parityTest + diff --git a/client/cmdlfcotag.c b/client/cmdlfcotag.c index 6d035e8c..d1808c9a 100644 --- a/client/cmdlfcotag.c +++ b/client/cmdlfcotag.c @@ -7,14 +7,15 @@ //----------------------------------------------------------------------------- // Low frequency COTAG commands //----------------------------------------------------------------------------- + +#include "cmdlfcotag.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmddata.h" -#include "data.h" -#include "cmdlfcotag.h" #include "lfdemod.h" #include "usb_cmd.h" #include "cmdmain.h" @@ -99,10 +100,9 @@ int CmdCOTAGRead(const char *Cmd) { getSamples(0, true); break; } case 1: { - GetFromBigBuf(DemodBuffer, COTAG_BITS, 0); - DemodBufferLen = COTAG_BITS; UsbCommand response; - if ( !WaitForResponseTimeout(CMD_ACK, &response, 1000) ) { + DemodBufferLen = COTAG_BITS; + if (!GetFromBigBuf(DemodBuffer, COTAG_BITS, 0, &response, 1000, true)) { PrintAndLog("timeout while waiting for reply."); return -1; } diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index 013c2f3f..cdaeb5ed 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -8,14 +8,14 @@ // Low frequency EM4x commands //----------------------------------------------------------------------------- +#include "cmdlfem4x.h" + #include #include #include -#include "cmdlfem4x.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" -#include "data.h" #include "graph.h" #include "cmdparser.h" #include "cmddata.h" @@ -335,7 +335,7 @@ int CmdEM410xBrute(const char *Cmd) delay = param_get32ex(Cmd, 4, 1000, 10); } - param_getstr(Cmd, 0, filename); + param_getstr(Cmd, 0, filename, sizeof(filename)); uidBlock = calloc(stUidBlock, 5); if (uidBlock == NULL) return 1; @@ -606,7 +606,7 @@ int EM4x50Read(const char *Cmd, bool verbose) char tmp2[20]; int phaseoff; high = low = 0; - memset(tmpbuff, 0, MAX_GRAPH_TRACE_LEN / 64); + memset(tmpbuff, 0, sizeof(tmpbuff)); // get user entry if any sscanf(Cmd, "%i %i", &clk, &invert); @@ -804,8 +804,7 @@ int usage_lf_em_read(void) { bool downloadSamplesEM() { // 8 bit preamble + 32 bit word response (max clock (128) * 40bits = 5120 samples) uint8_t got[6000]; - GetFromBigBuf(got, sizeof(got), 0); - if ( !WaitForResponseTimeout(CMD_ACK, NULL, 4000) ) { + if (!GetFromBigBuf(got, sizeof(got), 0, NULL, 4000, true)) { PrintAndLog("command execution time out"); return false; } @@ -950,7 +949,6 @@ int EM4x05ReadWord_ext(uint8_t addr, uint32_t pwd, bool usePwd, uint32_t *wordDa } int testLen = (GraphTraceLen < 1000) ? GraphTraceLen : 1000; if (graphJustNoise(GraphBuffer, testLen)) { - PrintAndLog("no tag not found"); return -1; } //attempt demod: diff --git a/client/cmdlffdx.c b/client/cmdlffdx.c index 2e2ee9fd..20f834ce 100644 --- a/client/cmdlffdx.c +++ b/client/cmdlffdx.c @@ -13,7 +13,7 @@ #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" // for PrintAndLog #include "util.h" #include "cmdparser.h" diff --git a/client/cmdlfhid.c b/client/cmdlfhid.c index 19b5a142..38e7073c 100644 --- a/client/cmdlfhid.c +++ b/client/cmdlfhid.c @@ -6,19 +6,136 @@ // the license. //----------------------------------------------------------------------------- // Low frequency HID commands (known) +// +// Useful resources: +// RF interface, programming a T55x7 clone, 26-bit HID H10301 encoding: +// http://www.proxmark.org/files/Documents/125%20kHz%20-%20HID/HID_format_example.pdf +// +// "Understanding Card Data Formats" +// https://www.hidglobal.com/sites/default/files/hid-understanding_card_data_formats-wp-en.pdf +// +// "What Format Do You Need?" +// https://www.hidglobal.com/sites/default/files/resource_files/hid-prox-br-en.pdf //----------------------------------------------------------------------------- +#include "cmdlfhid.h" + #include #include -#include "cmdlfhid.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "graph.h" #include "cmdparser.h" #include "cmddata.h" //for g_debugMode, demodbuff cmds #include "lfdemod.h" // for HIDdemodFSK +#include "hidcardformats.h" +#include "hidcardformatutils.h" +#include "util.h" // for param_get8,32,64 + + +/** + * Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers, one nibble + * at a time. + * + * Returns the number of nibbles (4 bits) entered. + */ +int hexstring_to_int96(/* out */ uint32_t* hi2,/* out */ uint32_t* hi, /* out */ uint32_t* lo, const char* str) { + // TODO: Replace this with param_gethex when it supports arbitrary length + // inputs. + int n = 0, i = 0; + + while (sscanf(&str[i++], "%1x", &n ) == 1) { + *hi2 = (*hi2 << 4) | (*hi >> 28); + *hi = (*hi << 4) | (*lo >> 28); + *lo = (*lo << 4) | (n & 0xf); + } + + return i - 1; +} + +void usage_encode(){ + PrintAndLog("Usage: lf hid encode [ ] {...}"); + PrintAndLog(" Fields: c: Card number"); + PrintAndLog(" f: Facility code"); + PrintAndLog(" i: Issue Level"); + PrintAndLog(" o: OEM code"); + PrintAndLog(" example: lf hid encode H10301 f 123 c 4567"); +} +void PrintProxTagId(hidproxmessage_t *packed){ + if (packed->top != 0) { + PrintAndLog("HID Prox TAG ID: %x%08x%08x", + (uint32_t)packed->top, (uint32_t)packed->mid, (uint32_t)packed->bot); + } else { + PrintAndLog("HID Prox TAG ID: %x%08x", + (uint32_t)packed->mid, (uint32_t)packed->bot); + } +} +bool Encode(/* in */ const char *Cmd, /* out */ hidproxmessage_t *packed){ + int formatIndex = -1; + char format[16]; + memset(format, 0, sizeof(format)); + if (!strcmp(Cmd, "help") || !strcmp(Cmd, "h") || !strcmp(Cmd, "list") || !strcmp(Cmd, "?")){ + usage_encode(); + return false; + } else { + param_getstr(Cmd, 0, format, sizeof(format)); + formatIndex = HIDFindCardFormat(format); + if (formatIndex == -1) { + printf("Unknown format: %s\r\n", format); + return false; + } + } + hidproxcard_t data; + memset(&data, 0, sizeof(hidproxcard_t)); + uint8_t cmdp = 1; + while(param_getchar(Cmd, cmdp) != 0x00) { + switch(param_getchar(Cmd, cmdp)) { + case 'I': + case 'i': + data.IssueLevel = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'F': + case 'f': + data.FacilityCode = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'C': + case 'c': + data.CardNumber = param_get64ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'o': + case 'O': + data.OEM = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + return false; + } + } + memset(packed, 0, sizeof(hidproxmessage_t)); + if (!HIDPack(formatIndex, &data, packed)) + { + PrintAndLog("The card data could not be encoded in the selected format."); + return false; + } else { + return true; + } + +} +void Write(hidproxmessage_t *packed){ + UsbCommand c; + c.d.asBytes[0] = (packed->top != 0 && ((packed->mid & 0xFFFFFFC0) != 0)) + ? 1 : 0; // Writing long format? + c.cmd = CMD_HID_CLONE_TAG; + c.arg[0] = (packed->top & 0x000FFFFF); + c.arg[1] = packed->mid; + c.arg[2] = packed->bot; + SendCommand(&c); +} -static int CmdHelp(const char *Cmd); //by marshmellow (based on existing demod + holiman's refactor) //HID Prox demod - FSK RF/50 with preamble of 00011101 (then manchester encoded) @@ -54,50 +171,13 @@ int CmdFSKdemodHID(const char *Cmd) if (g_debugMode) PrintAndLog("DEBUG: Error - no values found"); return 0; } - if (hi2 != 0){ //extra large HID tags - PrintAndLog("HID Prox TAG ID: %x%08x%08x (%d)", - (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - } - else { //standard HID tags <38 bits - uint8_t fmtLen = 0; - uint32_t fc = 0; - uint32_t cardnum = 0; - if (((hi>>5)&1)==1){//if bit 38 is set then < 37 bit format is used - uint32_t lo2=0; - lo2=(((hi & 31) << 12) | (lo>>20)); //get bits 21-37 to check for format len bit - uint8_t idx3 = 1; - while(lo2>1){ //find last bit set to 1 (format len bit) - lo2=lo2>>1; - idx3++; - } - fmtLen =idx3+19; - fc =0; - cardnum=0; - if(fmtLen==26){ - cardnum = (lo>>1)&0xFFFF; - fc = (lo>>17)&0xFF; - } - if(fmtLen==34){ - cardnum = (lo>>1)&0xFFFF; - fc= ((hi&1)<<15)|(lo>>17); - } - if(fmtLen==35){ - cardnum = (lo>>1)&0xFFFFF; - fc = ((hi&1)<<11)|(lo>>21); - } - } - else { //if bit 38 is not set then 37 bit format is used - fmtLen = 37; - fc = 0; - cardnum = 0; - if(fmtLen == 37){ - cardnum = (lo>>1)&0x7FFFF; - fc = ((hi&0xF)<<12)|(lo>>20); - } - } - PrintAndLog("HID Prox TAG ID: %x%08x (%d) - Format Len: %dbit - FC: %d - Card: %d", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF, - (unsigned int) fmtLen, (unsigned int) fc, (unsigned int) cardnum); + + hidproxmessage_t packed = initialize_proxmessage_object(hi2, hi, lo); + PrintProxTagId(&packed); + + bool ret = HIDTryUnpack(&packed, false); + if (!ret) { + PrintAndLog("Invalid or unsupported tag length."); } setDemodBuf(BitStream,BitLen,idx); setClockGrid(50, waveIdx + (idx*50)); @@ -111,7 +191,7 @@ int CmdFSKdemodHID(const char *Cmd) int CmdHIDReadFSK(const char *Cmd) { int findone=0; - if(Cmd[0]=='1') findone=1; + if(Cmd[0]=='1') findone=1; UsbCommand c={CMD_HID_DEMOD_FSK}; c.arg[0]=findone; SendCommand(&c); @@ -120,67 +200,92 @@ int CmdHIDReadFSK(const char *Cmd) int CmdHIDSim(const char *Cmd) { - uint32_t hi = 0, lo = 0; - int n = 0, i = 0; - - while (sscanf(&Cmd[i++], "%1x", &n ) == 1) { - hi = (hi << 4) | (lo >> 28); - lo = (lo << 4) | (n & 0xf); - } - - PrintAndLog("Emulating tag with ID %x%08x", hi, lo); - PrintAndLog("Press pm3-button to abort simulation"); - - UsbCommand c = {CMD_HID_SIM_TAG, {hi, lo, 0}}; - SendCommand(&c); - return 0; -} - -int CmdHIDClone(const char *Cmd) -{ - unsigned int hi2 = 0, hi = 0, lo = 0; - int n = 0, i = 0; - UsbCommand c; - - if (strchr(Cmd,'l') != 0) { - while (sscanf(&Cmd[i++], "%1x", &n ) == 1) { - hi2 = (hi2 << 4) | (hi >> 28); - hi = (hi << 4) | (lo >> 28); - lo = (lo << 4) | (n & 0xf); - } - - PrintAndLog("Cloning tag with long ID %x%08x%08x", hi2, hi, lo); - - c.d.asBytes[0] = 1; - } - else { - while (sscanf(&Cmd[i++], "%1x", &n ) == 1) { - hi = (hi << 4) | (lo >> 28); - lo = (lo << 4) | (n & 0xf); - } - - PrintAndLog("Cloning tag with ID %x%08x", hi, lo); - - hi2 = 0; - c.d.asBytes[0] = 0; + uint32_t hi2 = 0, hi = 0, lo = 0; + hexstring_to_int96(&hi2, &hi, &lo, Cmd); + if (hi2 != 0) { + PrintAndLog("Emulating tag with ID %x%08x%08x", hi2, hi, lo); + } else { + PrintAndLog("Emulating tag with ID %x%08x", hi, lo); } - c.cmd = CMD_HID_CLONE_TAG; - c.arg[0] = hi2; - c.arg[1] = hi; - c.arg[2] = lo; + PrintAndLog("Press pm3-button to abort simulation"); + UsbCommand c = {CMD_HID_SIM_TAG, {hi2, hi, lo}}; SendCommand(&c); return 0; } +int CmdHIDClone(const char *Cmd) +{ + unsigned int top = 0, mid = 0, bot = 0; + hexstring_to_int96(&top, &mid, &bot, Cmd); + hidproxmessage_t packed = initialize_proxmessage_object(top, mid, bot); + Write(&packed); + return 0; +} + +int CmdHIDDecode(const char *Cmd){ + if (strlen(Cmd)<3) { + PrintAndLog("Usage: lf hid decode {p}"); + PrintAndLog(" (optional) p: Ignore invalid parity"); + PrintAndLog(" sample: lf hid decode 2006f623ae"); + return 0; + } + + uint32_t top = 0, mid = 0, bot = 0; + bool ignoreParity = false; + hexstring_to_int96(&top, &mid, &bot, Cmd); + hidproxmessage_t packed = initialize_proxmessage_object(top, mid, bot); + + char opt = param_getchar(Cmd, 1); + if (opt == 'p') ignoreParity = true; + + HIDTryUnpack(&packed, ignoreParity); + return 0; +} +int CmdHIDEncode(const char *Cmd) { + if (strlen(Cmd) == 0) { + usage_encode(); + return 0; + } + + hidproxmessage_t packed; + memset(&packed, 0, sizeof(hidproxmessage_t)); + if (Encode(Cmd, &packed)) + PrintProxTagId(&packed); + return 0; +} + +int CmdHIDWrite(const char *Cmd) { + if (strlen(Cmd) == 0) { + usage_encode(); + return 0; + } + hidproxmessage_t packed; + memset(&packed, 0, sizeof(hidproxmessage_t)); + if (Encode(Cmd, &packed)){ + PrintProxTagId(&packed); + Write(&packed); + } + return 0; +} + +int CmdHIDFormats(){ + HIDListFormats(); + return 0; +} +static int CmdHelp(const char *Cmd); // define this now so the below won't error out. static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"demod", CmdFSKdemodHID, 1, "Demodulate HID Prox from GraphBuffer"}, {"read", CmdHIDReadFSK, 0, "['1'] Realtime HID FSK Read from antenna (option '1' for one tag only)"}, {"sim", CmdHIDSim, 0, " -- HID tag simulator"}, - {"clone", CmdHIDClone, 0, " ['l'] -- Clone HID to T55x7 (tag must be in antenna)(option 'l' for 84bit ID)"}, + {"clone", CmdHIDClone, 0, " -- Clone HID to T55x7 (tag must be in antenna)"}, + {"decode", CmdHIDDecode, 1, " -- Try to decode an HID tag and show its contents"}, + {"encode", CmdHIDEncode, 1, " -- Encode an HID ID with the specified format and fields"}, + {"formats", CmdHIDFormats, 1, "List supported card formats"}, + {"write", CmdHIDWrite, 0, " -- Encode and write to a T55x7 tag (tag must be in antenna)"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdlfhid.h b/client/cmdlfhid.h index b03f7041..ef907f67 100644 --- a/client/cmdlfhid.h +++ b/client/cmdlfhid.h @@ -11,10 +11,15 @@ #ifndef CMDLFHID_H__ #define CMDLFHID_H__ +#include +#include + int CmdLFHID(const char *Cmd); int CmdFSKdemodHID(const char *Cmd); int CmdHIDReadDemod(const char *Cmd); int CmdHIDSim(const char *Cmd); int CmdHIDClone(const char *Cmd); - +int CmdHIDDecode(const char *Cmd); +int CmdHIDEncode(const char *Cmd); +int CmdHIDWrite(const char *Cmd); #endif diff --git a/client/cmdlfhitag.c b/client/cmdlfhitag.c index 103f2b77..8eef2359 100644 --- a/client/cmdlfhitag.c +++ b/client/cmdlfhitag.c @@ -8,11 +8,12 @@ // Low frequency Hitag support //----------------------------------------------------------------------------- +#include "cmdlfhitag.h" + #include #include #include -#include "data.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "cmdparser.h" #include "common.h" @@ -34,8 +35,7 @@ int CmdLFHitagList(const char *Cmd) // Query for the actual size of the trace UsbCommand response; - GetFromBigBuf(got, USB_CMD_DATA_SIZE, 0); - WaitForResponse(CMD_ACK, &response); + GetFromBigBuf(got, USB_CMD_DATA_SIZE, 0, &response, -1, false); uint16_t traceLen = response.arg[2]; if (traceLen > USB_CMD_DATA_SIZE) { uint8_t *p = realloc(got, traceLen); @@ -45,8 +45,7 @@ int CmdLFHitagList(const char *Cmd) return 2; } got = p; - GetFromBigBuf(got, traceLen, 0); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, traceLen, 0, NULL, -1, false); } PrintAndLog("recorded activity (TraceLen = %d bytes):"); @@ -67,6 +66,7 @@ int CmdLFHitagList(const char *Cmd) if (strlen(filename) > 0) { if ((pf = fopen(filename,"wb")) == NULL) { PrintAndLog("Error: Could not open file [%s]",filename); + free(got); return 1; } } @@ -167,11 +167,11 @@ int CmdLFHitagSim(const char *Cmd) { return 1; } tag_mem_supplied = true; - if (fread(c.d.asBytes,48,1,pf) == 0) { - PrintAndLog("Error: File reading error"); - fclose(pf); + if (fread(c.d.asBytes,1,48,pf) != 48) { + PrintAndLog("Error: File reading error"); + fclose(pf); return 1; - } + } fclose(pf); } else { tag_mem_supplied = false; @@ -239,6 +239,7 @@ int CmdLFHitagReader(const char *Cmd) { c.arg[0] = htf; // Send the command to the proxmark + clearCommandBuffer(); SendCommand(&c); UsbCommand resp; @@ -289,7 +290,7 @@ int CmdLFHitagSimS(const char *Cmd) { return 1; } tag_mem_supplied = true; - if (fread(c.d.asBytes, 4*64, 1, pf) == 0) { + if (fread(c.d.asBytes, 1, 4*64, pf) != 4*64) { PrintAndLog("Error: File reading error"); fclose(pf); return 1; @@ -321,9 +322,9 @@ int CmdLFHitagCheckChallenges(const char *Cmd) { return 1; } file_given = true; - if (fread(c.d.asBytes,8*60,1,pf) == 0) { - PrintAndLog("Error: File reading error"); - fclose(pf); + if (fread(c.d.asBytes,1,8*60,pf) != 8*60) { + PrintAndLog("Error: File reading error"); + fclose(pf); return 1; } fclose(pf); diff --git a/client/cmdlfindala.c b/client/cmdlfindala.c index 0a4f7834..02c906d0 100644 --- a/client/cmdlfindala.c +++ b/client/cmdlfindala.c @@ -8,10 +8,11 @@ // PSK1, rf/32, 64 or 224 bits (known) //----------------------------------------------------------------------------- +#include "cmdlfindala.h" + #include #include -#include "cmdlfindala.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "graph.h" #include "cmdparser.h" @@ -286,7 +287,7 @@ int CmdIndalaDemod(const char *Cmd) { } int CmdIndalaClone(const char *Cmd) { - UsbCommand c; + UsbCommand c = {0}; unsigned int uid1, uid2, uid3, uid4, uid5, uid6, uid7; uid1 = uid2 = uid3 = uid4 = uid5 = uid6 = uid7 = 0; diff --git a/client/cmdlfio.c b/client/cmdlfio.c index 4ccd5538..feb7d373 100644 --- a/client/cmdlfio.c +++ b/client/cmdlfio.c @@ -8,14 +8,14 @@ // FSK2a, rf/64, 64 bits (complete) //----------------------------------------------------------------------------- +#include "cmdlfio.h" + #include #include #include #include #include -#include "cmdlfio.h" -#include "proxmark3.h" -#include "data.h" +#include "comms.h" #include "graph.h" #include "ui.h" #include "cmdparser.h" diff --git a/client/cmdlfjablotron.c b/client/cmdlfjablotron.c index 9c69099e..4756266c 100644 --- a/client/cmdlfjablotron.c +++ b/client/cmdlfjablotron.c @@ -9,10 +9,11 @@ //----------------------------------------------------------------------------- #include "cmdlfjablotron.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfnexwatch.c b/client/cmdlfnexwatch.c index caabe835..d44c2a27 100644 --- a/client/cmdlfnexwatch.c +++ b/client/cmdlfnexwatch.c @@ -7,12 +7,14 @@ // Low frequency Honeywell NexWatch tag commands // PSK1 RF/16, RF/2, 128 bits long (known) //----------------------------------------------------------------------------- + +#include "cmdlfnexwatch.h" + #include #include #include #include -#include "cmdlfnexwatch.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfnoralsy.c b/client/cmdlfnoralsy.c index 2c90fa14..52c35081 100644 --- a/client/cmdlfnoralsy.c +++ b/client/cmdlfnoralsy.c @@ -7,11 +7,13 @@ // Low frequency Noralsy tag commands // ASK/Manchester, STT, RF/32, 96 bits long (some bits unknown) //----------------------------------------------------------------------------- + #include "cmdlfnoralsy.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfpac.c b/client/cmdlfpac.c index ef6b394b..190361ec 100644 --- a/client/cmdlfpac.c +++ b/client/cmdlfpac.c @@ -7,10 +7,12 @@ // Low frequency Stanley/PAC tag commands // NRZ, RF/32, 128 bits long (unknown cs) //----------------------------------------------------------------------------- + #include "cmdlfpac.h" + #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfpcf7931.c b/client/cmdlfpcf7931.c index ffaf946f..9c694b8d 100644 --- a/client/cmdlfpcf7931.c +++ b/client/cmdlfpcf7931.c @@ -8,9 +8,12 @@ //----------------------------------------------------------------------------- // Low frequency PCF7931 commands //----------------------------------------------------------------------------- + +#include "cmdlfpcf7931.h" + #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" @@ -18,7 +21,6 @@ #include "cmddata.h" #include "cmdmain.h" #include "cmdlf.h" -#include "cmdlfpcf7931.h" static int CmdHelp(const char *Cmd); diff --git a/client/cmdlfpcf7931.h b/client/cmdlfpcf7931.h index e093039c..8b24f03a 100644 --- a/client/cmdlfpcf7931.h +++ b/client/cmdlfpcf7931.h @@ -12,6 +12,8 @@ #ifndef CMDLFPCF7931_H__ #define CMDLFPCF7931_H__ +#include + struct pcf7931_config{ uint8_t Pwd[7]; uint16_t InitDelay; diff --git a/client/cmdlfpresco.c b/client/cmdlfpresco.c index 29fc81f2..865b384b 100644 --- a/client/cmdlfpresco.c +++ b/client/cmdlfpresco.c @@ -7,11 +7,13 @@ // Low frequency Presco tag commands // ASK/Manchester, rf/32, 128 bits (complete) //----------------------------------------------------------------------------- + +#include "cmdlfpresco.h" + #include #include #include -#include "cmdlfpresco.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" @@ -71,7 +73,7 @@ int GetWiegandFromPresco(const char *Cmd, uint32_t *sitecode, uint32_t *usercode case 'D': case 'd': //param get string int param_getstr(const char *line, int paramnum, char * str) - stringlen = param_getstr(Cmd, cmdp+1, id); + stringlen = param_getstr(Cmd, cmdp+1, id, sizeof(id)); if (stringlen < 2) return -1; cmdp+=2; break; diff --git a/client/cmdlfpyramid.c b/client/cmdlfpyramid.c index 366889f3..82367720 100644 --- a/client/cmdlfpyramid.c +++ b/client/cmdlfpyramid.c @@ -7,11 +7,13 @@ // Low frequency Farpoint / Pyramid tag commands // FSK2a, rf/50, 128 bits (complete) //----------------------------------------------------------------------------- + +#include "cmdlfpyramid.h" + #include #include #include -#include "cmdlfpyramid.h" -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfsecurakey.c b/client/cmdlfsecurakey.c index 8ae81250..44b77060 100644 --- a/client/cmdlfsecurakey.c +++ b/client/cmdlfsecurakey.c @@ -7,11 +7,13 @@ // Low frequency Securakey tag commands // ASK/Manchester, RF/40, 96 bits long (unknown cs) //----------------------------------------------------------------------------- + #include "cmdlfsecurakey.h" + #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 252aaaa2..2a096cd0 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -7,21 +7,21 @@ // Low frequency T55xx commands //----------------------------------------------------------------------------- +#include "cmdlft55xx.h" + #include #include #include #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "graph.h" #include "cmdmain.h" #include "cmdparser.h" #include "cmddata.h" #include "cmdlf.h" -#include "cmdlft55xx.h" #include "util.h" -#include "data.h" #include "lfdemod.h" #include "cmdhf14a.h" //for getTagInfo #include "protocols.h" @@ -207,7 +207,7 @@ void printT5xxHeader(uint8_t page){ int CmdT55xxSetConfig(const char *Cmd) { uint8_t offset = 0; - char modulation[5] = {0x00}; + char modulation[6] = {0x00}; char tmp = 0x00; uint8_t bitRate = 0; uint8_t rates[9] = {8,16,32,40,50,64,100,128,0}; @@ -236,7 +236,7 @@ int CmdT55xxSetConfig(const char *Cmd) { cmdp+=2; break; case 'd': - param_getstr(Cmd, cmdp+1, modulation); + param_getstr(Cmd, cmdp+1, modulation, sizeof(modulation)); cmdp += 2; if ( strcmp(modulation, "FSK" ) == 0) { @@ -1355,8 +1355,7 @@ int CmdResetRead(const char *Cmd) { } uint8_t got[BIGBUF_SIZE-1]; - GetFromBigBuf(got,sizeof(got),0); - WaitForResponse(CMD_ACK,NULL); + GetFromBigBuf(got, sizeof(got), 0, NULL, -1 , 0); setGraphBuf(got, sizeof(got)); return 1; } @@ -1433,7 +1432,7 @@ int CmdT55xxBruteForce(const char *Cmd) { //The line start with # is comment, skip if( buf[0]=='#' ) continue; - if (!isxdigit(buf[0])) { + if (!isxdigit((unsigned char)buf[0])) { PrintAndLog("File content error. '%s' must include 8 HEX symbols", buf); continue; } diff --git a/client/cmdlft55xx.h b/client/cmdlft55xx.h index 4f0fd21d..2ae3e69b 100644 --- a/client/cmdlft55xx.h +++ b/client/cmdlft55xx.h @@ -10,6 +10,9 @@ #ifndef CMDLFT55XX_H__ #define CMDLFT55XX_H__ +#include +#include + typedef struct { uint32_t bl1; uint32_t bl2; diff --git a/client/cmdlfti.c b/client/cmdlfti.c index 5dae9c0e..ff463971 100644 --- a/client/cmdlfti.c +++ b/client/cmdlfti.c @@ -8,16 +8,17 @@ // Low frequency TI commands //----------------------------------------------------------------------------- +#include "cmdlfti.h" + #include #include #include #include "crc16.h" -#include "proxmark3.h" -#include "data.h" +#include "comms.h" #include "ui.h" #include "graph.h" #include "cmdparser.h" -#include "cmdlfti.h" +#include "util.h" static int CmdHelp(const char *Cmd); diff --git a/client/cmdlfviking.c b/client/cmdlfviking.c index 779156c8..73a9126a 100644 --- a/client/cmdlfviking.c +++ b/client/cmdlfviking.c @@ -7,11 +7,13 @@ // Low frequency Viking tag commands (AKA FDI Matalec Transit) // ASK/Manchester, RF/32, 64 bits (complete) //----------------------------------------------------------------------------- + +#include "cmdlfviking.h" + #include #include #include -#include "proxmark3.h" -#include "cmdlfviking.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdlfvisa2000.c b/client/cmdlfvisa2000.c index 04589ba5..33259794 100644 --- a/client/cmdlfvisa2000.c +++ b/client/cmdlfvisa2000.c @@ -13,7 +13,7 @@ #include #include -#include "proxmark3.h" +#include "comms.h" #include "ui.h" #include "util.h" #include "graph.h" diff --git a/client/cmdmain.c b/client/cmdmain.c index f407cee4..f503021a 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -8,39 +8,31 @@ // Main command parser entry point //----------------------------------------------------------------------------- +#include "cmdmain.h" + +#include #include #include #include #include #include "cmdparser.h" #include "proxmark3.h" -#include "data.h" #include "usb_cmd.h" #include "ui.h" #include "cmdhf.h" #include "cmddata.h" #include "cmdhw.h" #include "cmdlf.h" -#include "cmdmain.h" #include "util.h" #include "util_posix.h" #include "cmdscript.h" -#include "cmdcrc.h" - - -unsigned int current_command = CMD_UNKNOWN; +#ifdef WITH_SMARTCARD + #include "cmdsmartcard.h" +#endif static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); -static int CmdRev(const char *Cmd); -//For storing command that are received from the device -#define CMD_BUFFER_SIZE 50 -static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; -//Points to the next empty position to write to -static int cmd_head;//Starts as 0 -//Points to the position of the last unread command -static int cmd_tail;//Starts as 0 static command_t CommandTable[] = { @@ -49,7 +41,9 @@ static command_t CommandTable[] = {"hf", CmdHF, 1, "{ High Frequency commands... }"}, {"hw", CmdHW, 1, "{ Hardware commands... }"}, {"lf", CmdLF, 1, "{ Low Frequency commands... }"}, - {"reveng",CmdRev, 1, "Crc calculations from the software reveng1-30"}, +#ifdef WITH_SMARTCARD + {"sc", CmdSmartcard,1,"{ Smartcard commands... }"}, +#endif {"script",CmdScript,1, "{ Scripting commands }"}, {"quit", CmdQuit, 1, "Exit program"}, {"exit", CmdQuit, 1, "Exit program"}, @@ -60,115 +54,18 @@ command_t* getTopLevelCommandTable() { return CommandTable; } -int CmdHelp(const char *Cmd) + +static int CmdHelp(const char *Cmd) { CmdsHelp(CommandTable); return 0; } -int CmdQuit(const char *Cmd) +static int CmdQuit(const char *Cmd) { return 99; } -int CmdRev(const char *Cmd) -{ - CmdCrc(Cmd); - return 0; -} - -/** - * @brief This method should be called when sending a new command to the pm3. In case any old - * responses from previous commands are stored in the buffer, a call to this method should clear them. - * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which - * operation. Right now we'll just have to live with this. - */ -void clearCommandBuffer() -{ - //This is a very simple operation - cmd_tail = cmd_head; -} - -/** - * @brief storeCommand stores a USB command in a circular buffer - * @param UC - */ -void storeCommand(UsbCommand *command) -{ - if( ( cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) - { - //If these two are equal, we're about to overwrite in the - // circular buffer. - PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); - } - //Store the command at the 'head' location - UsbCommand* destination = &cmdBuffer[cmd_head]; - memcpy(destination, command, sizeof(UsbCommand)); - - cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap -} - - -/** - * @brief getCommand gets a command from an internal circular buffer. - * @param response location to write command - * @return 1 if response was returned, 0 if nothing has been received - */ -int getCommand(UsbCommand* response) -{ - //If head == tail, there's nothing to read, or if we just got initialized - if(cmd_head == cmd_tail){ - return 0; - } - //Pick out the next unread command - UsbCommand* last_unread = &cmdBuffer[cmd_tail]; - memcpy(response, last_unread, sizeof(UsbCommand)); - //Increment tail - this is a circular buffer, so modulo buffer size - cmd_tail = (cmd_tail +1 ) % CMD_BUFFER_SIZE; - - return 1; -} - - -/** - * Waits for a certain response type. This method waits for a maximum of - * ms_timeout milliseconds for a specified response command. - *@brief WaitForResponseTimeout - * @param cmd command to wait for - * @param response struct to copy received command into. - * @param ms_timeout - * @return true if command was returned, otherwise false - */ -bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { - - UsbCommand resp; - - if (response == NULL) { - response = &resp; - } - - // Wait until the command is received - for(size_t dm_seconds=0; dm_seconds < ms_timeout/10; dm_seconds++) { - while(getCommand(response)) { - if(response->cmd == cmd){ - return true; - } - } - msleep(10); // XXX ugh - if (dm_seconds == 200) { // Two seconds elapsed - PrintAndLog("Waiting for a response from the proxmark..."); - PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); - } - } - return false; -} - - -bool WaitForResponse(uint32_t cmd, UsbCommand* response) { - return WaitForResponseTimeout(cmd,response,-1); -} - - //----------------------------------------------------------------------------- // Entry point into our code: called whenever the user types a command and // then presses Enter, which the full command line that they typed. @@ -177,38 +74,3 @@ int CommandReceived(char *Cmd) { return CmdsParse(CommandTable, Cmd); } - -//----------------------------------------------------------------------------- -// Entry point into our code: called whenever we received a packet over USB -// that we weren't necessarily expecting, for example a debug print. -//----------------------------------------------------------------------------- -void UsbCommandReceived(UsbCommand *UC) -{ - switch(UC->cmd) { - // First check if we are handling a debug message - case CMD_DEBUG_PRINT_STRING: { - char s[USB_CMD_DATA_SIZE+1]; - memset(s, 0x00, sizeof(s)); - size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); - memcpy(s,UC->d.asBytes,len); - PrintAndLog("#db# %s", s); - return; - } break; - - case CMD_DEBUG_PRINT_INTEGERS: { - PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); - return; - } break; - - case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { - memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); - return; - } break; - - default: - storeCommand(UC); - break; - } - -} - diff --git a/client/cmdmain.h b/client/cmdmain.h index 0de3f392..42b49145 100644 --- a/client/cmdmain.h +++ b/client/cmdmain.h @@ -11,16 +11,9 @@ #ifndef CMDMAIN_H__ #define CMDMAIN_H__ -#include -#include -#include "usb_cmd.h" #include "cmdparser.h" -extern void UsbCommandReceived(UsbCommand *UC); extern int CommandReceived(char *Cmd); -extern bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); -extern bool WaitForResponse(uint32_t cmd, UsbCommand* response); -extern void clearCommandBuffer(); extern command_t* getTopLevelCommandTable(); #endif diff --git a/client/cmdparser.c b/client/cmdparser.c index 32508997..34230d52 100644 --- a/client/cmdparser.c +++ b/client/cmdparser.c @@ -8,12 +8,14 @@ // Command parser //----------------------------------------------------------------------------- +#include "cmdparser.h" + #include #include #include #include "ui.h" -#include "cmdparser.h" #include "proxmark3.h" +#include "comms.h" void CmdsHelp(const command_t Commands[]) @@ -23,7 +25,7 @@ void CmdsHelp(const command_t Commands[]) int i = 0; while (Commands[i].Name) { - if (!offline || Commands[i].Offline) + if (!IsOffline() || Commands[i].Offline) PrintAndLog("%-16s %s", Commands[i].Name, Commands[i].Help); ++i; } diff --git a/client/cmdscript.c b/client/cmdscript.c index 23163aa9..7b0e9000 100644 --- a/client/cmdscript.c +++ b/client/cmdscript.c @@ -17,7 +17,6 @@ #include "proxmark3.h" #include "scripting.h" -#include "data.h" #include "ui.h" #include "graph.h" #include "cmdparser.h" @@ -76,8 +75,11 @@ int CmdList(const char *Cmd) { DIR *dp; struct dirent *ep; - char script_directory_path[strlen(get_my_executable_directory()) + strlen(LUA_SCRIPTS_DIRECTORY) + 1]; - strcpy(script_directory_path, get_my_executable_directory()); + char const * exedir = get_my_executable_directory(); + if (exedir == NULL) + return 0; + char script_directory_path[strlen(exedir) + strlen(LUA_SCRIPTS_DIRECTORY) + 1]; + strcpy(script_directory_path, exedir); strcat(script_directory_path, LUA_SCRIPTS_DIRECTORY); dp = opendir(script_directory_path); diff --git a/client/cmdsmartcard.c b/client/cmdsmartcard.c new file mode 100644 index 00000000..b2a5705d --- /dev/null +++ b/client/cmdsmartcard.c @@ -0,0 +1,707 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 Smartcard module commands +//----------------------------------------------------------------------------- +#include "cmdsmartcard.h" +#include "smartcard.h" +#include "comms.h" +#include "protocols.h" + + +static int CmdHelp(const char *Cmd); + +int usage_sm_raw(void) { + PrintAndLog("Usage: sc raw [h|r|c] d <0A 0B 0C ... hex>"); + PrintAndLog(" h : this help"); + PrintAndLog(" r : do not read response"); + PrintAndLog(" a : active signal field ON without select"); + PrintAndLog(" s : active signal field ON with select"); + PrintAndLog(" t : executes TLV decoder if it is possible"); + PrintAndLog(" d : bytes to send"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc raw d 11223344"); + return 0; +} +int usage_sm_reader(void) { + PrintAndLog("Usage: sc reader [h|s]"); + PrintAndLog(" h : this help"); + PrintAndLog(" s : silent (no messages)"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc reader"); + return 0; +} +int usage_sm_info(void) { + PrintAndLog("Usage: sc info [h|s]"); + PrintAndLog(" h : this help"); + PrintAndLog(" s : silent (no messages)"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc info"); + return 0; +} +int usage_sm_upgrade(void) { + PrintAndLog("Upgrade firmware"); + PrintAndLog("Usage: sc upgrade f "); + PrintAndLog(" h : this help"); + PrintAndLog(" f : firmware file name"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc upgrade f myfile"); + PrintAndLog(""); + PrintAndLog("WARNING - Dangerous command, do wrong and you will brick the smart card socket"); + return 0; +} +int usage_sm_setclock(void) { + PrintAndLog("Usage: sc setclock [h] c "); + PrintAndLog(" h : this help"); + PrintAndLog(" c <> : clockspeed (0 = 16mhz, 1=8mhz, 2=4mhz) "); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc setclock c 2"); + return 0; +} + +int CmdSmartRaw(const char *Cmd) { + + int hexlen = 0; + bool active = false; + bool active_select = false; + uint8_t cmdp = 0; + bool errors = false, reply = true, decodeTLV = false, breakloop = false; + uint8_t data[USB_CMD_DATA_SIZE] = {0x00}; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_raw(); + case 'r': + reply = false; + cmdp++; + break; + case 'a': + active = true; + cmdp++; + break; + case 's': + active_select = true; + cmdp++; + break; + case 't': + decodeTLV = true; + cmdp++; + break; + case 'd': { + switch (param_gethex_to_eol(Cmd, cmdp+1, data, sizeof(data), &hexlen)) { + case 1: + PrintAndLog("Invalid HEX value."); + return 1; + case 2: + PrintAndLog("Too many bytes. Max %d bytes", sizeof(data)); + return 1; + case 3: + PrintAndLog("Hex must have an even number of digits."); + return 1; + } + cmdp++; + breakloop = true; + break; + } + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + + if ( breakloop ) + break; + } + + //Validations + if (errors || cmdp == 0 ) return usage_sm_raw(); + + // arg0 = RFU flags + // arg1 = length + UsbCommand c = {CMD_SMART_RAW, {0, hexlen, 0}}; + + if (active || active_select) { + c.arg[0] |= SC_CONNECT; + if (active) + c.arg[0] |= SC_NO_SELECT; + } + + if (hexlen > 0) { + c.arg[0] |= SC_RAW; + } + + memcpy(c.d.asBytes, data, hexlen ); + clearCommandBuffer(); + SendCommand(&c); + + // reading response from smart card + if ( reply ) { + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + PrintAndLog("smart card response failed"); + return 1; + } + uint32_t datalen = resp.arg[0]; + + if ( !datalen ) { + PrintAndLog("smart card response failed"); + return 1; + } + + PrintAndLog("received %i bytes", datalen); + + if (!datalen) + return 1; + + uint8_t *data = resp.d.asBytes; + + // TLV decoder + if (decodeTLV ) { + + if (datalen >= 2) { + PrintAndLog("%02x %02x | %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); + } + if (datalen > 4) { + TLVPrintFromBuffer(data, datalen - 2); + } + } else { + PrintAndLog("%s", sprint_hex(data, datalen)); + } + } + return 0; +} + +int CmdSmartUpgrade(const char *Cmd) { + + PrintAndLog("WARNING - Smartcard socket firmware upgrade."); + PrintAndLog("Dangerous command, do wrong and you will brick the smart card socket"); + + FILE *f; + char filename[FILE_PATH_SIZE] = {0}; + uint8_t cmdp = 0; + bool errors = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'f': + //File handling and reading + if ( param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE ) { + PrintAndLog("Filename too long"); + errors = true; + break; + } + cmdp += 2; + break; + case 'h': + return usage_sm_upgrade(); + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors || cmdp == 0 ) return usage_sm_upgrade(); + + // load file + f = fopen(filename, "rb"); + if ( !f ) { + PrintAndLog("File: %s: not found or locked.", filename); + return 1; + } + + // get filesize in order to malloc memory + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize < 0) { + PrintAndLog("error, when getting filesize"); + fclose(f); + return 1; + } + + uint8_t *dump = calloc(fsize, sizeof(uint8_t)); + if (!dump) { + PrintAndLog("error, cannot allocate memory "); + fclose(f); + return 1; + } + + size_t bytes_read = fread(dump, 1, fsize, f); + if (f) + fclose(f); + + PrintAndLog("Smartcard socket firmware uploading to PM3"); + //Send to device + uint32_t index = 0; + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = bytes_read; + + while (bytes_remaining > 0){ + uint32_t bytes_in_packet = MIN(USB_CMD_DATA_SIZE, bytes_remaining); + UsbCommand c = {CMD_SMART_UPLOAD, {index + bytes_sent, bytes_in_packet, 0}}; + + // Fill usb bytes with 0xFF + memset(c.d.asBytes, 0xFF, USB_CMD_DATA_SIZE); + memcpy(c.d.asBytes, dump + bytes_sent, bytes_in_packet); + clearCommandBuffer(); + SendCommand(&c); + if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2000) ) { + PrintAndLog("timeout while waiting for reply."); + free(dump); + return 1; + } + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + printf("."); fflush(stdout); + } + free(dump); + printf("\n"); + PrintAndLog("Smartcard socket firmware updating, don\'t turn off your PM3!"); + + // trigger the firmware upgrade + UsbCommand c = {CMD_SMART_UPGRADE, {bytes_read, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + PrintAndLog("timeout while waiting for reply."); + return 1; + } + if ( (resp.arg[0] && 0xFF ) ) + PrintAndLog("Smartcard socket firmware upgraded successful"); + else + PrintAndLog("Smartcard socket firmware updating failed"); + return 0; +} + +int CmdSmartInfo(const char *Cmd){ + uint8_t cmdp = 0; + bool errors = false, silent = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_info(); + case 's': + silent = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + cmdp++; + } + + //Validations + if (errors ) return usage_sm_info(); + + UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + + smart_card_atr_t card; + memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t)); + + // print header + PrintAndLog("\n--- Smartcard Information ---------"); + PrintAndLog("-------------------------------------------------------------"); + PrintAndLog("ISO76183 ATR : %s", sprint_hex(card.atr, card.atr_len)); + PrintAndLog("look up ATR"); + PrintAndLog("http://smartcard-atr.appspot.com/parse?ATR=%s", sprint_hex_inrow(card.atr, card.atr_len) ); + return 0; +} + +int CmdSmartReader(const char *Cmd){ + uint8_t cmdp = 0; + bool errors = false, silent = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_reader(); + case 's': + silent = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + cmdp++; + } + + //Validations + if (errors ) return usage_sm_reader(); + + UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + smart_card_atr_t card; + memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t)); + PrintAndLog("ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); + return 0; +} + +int CmdSmartSetClock(const char *Cmd){ + uint8_t cmdp = 0; + bool errors = false; + uint8_t clock = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_setclock(); + case 'c': + clock = param_get8ex(Cmd, cmdp+1, 2, 10); + if ( clock > 2) + errors = true; + + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors || cmdp == 0) return usage_sm_setclock(); + + UsbCommand c = {CMD_SMART_SETCLOCK, {clock, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + PrintAndLog("smart card select failed"); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + PrintAndLog("smart card set clock failed"); + return 1; + } + + switch (clock) { + case 0: + PrintAndLog("Clock changed to 16mhz giving 10800 baudrate"); + break; + case 1: + PrintAndLog("Clock changed to 8mhz giving 21600 baudrate"); + break; + case 2: + PrintAndLog("Clock changed to 4mhz giving 86400 baudrate"); + break; + default: + break; + } + return 0; +} + + +// iso 7816-3 +void annotateIso7816(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize){ + // S-block + if ( (cmd[0] & 0xC0) && (cmdsize == 3) ) { + switch ( (cmd[0] & 0x3f) ) { + case 0x00 : snprintf(exp, size, "S-block RESYNCH req"); break; + case 0x20 : snprintf(exp, size, "S-block RESYNCH resp"); break; + case 0x01 : snprintf(exp, size, "S-block IFS req"); break; + case 0x21 : snprintf(exp, size, "S-block IFS resp"); break; + case 0x02 : snprintf(exp, size, "S-block ABORT req"); break; + case 0x22 : snprintf(exp, size, "S-block ABORT resp"); break; + case 0x03 : snprintf(exp, size, "S-block WTX reqt"); break; + case 0x23 : snprintf(exp, size, "S-block WTX resp"); break; + default : snprintf(exp, size, "S-block"); break; + } + } + // R-block (ack) + else if ( ((cmd[0] & 0xD0) == 0x80) && ( cmdsize > 2) ) { + if ( (cmd[0] & 0x10) == 0 ) + snprintf(exp, size, "R-block ACK"); + else + snprintf(exp, size, "R-block NACK"); + } + // I-block + else { + + int pos = (cmd[0] == 2 || cmd[0] == 3) ? 2 : 3; + switch ( cmd[pos] ) { + case ISO7816_READ_BINARY :snprintf(exp, size, "READ BIN");break; + case ISO7816_WRITE_BINARY :snprintf(exp, size, "WRITE BIN");break; + case ISO7816_UPDATE_BINARY :snprintf(exp, size, "UPDATE BIN");break; + case ISO7816_ERASE_BINARY :snprintf(exp, size, "ERASE BIN");break; + case ISO7816_READ_RECORDS :snprintf(exp, size, "READ RECORDS");break; + case ISO7816_WRITE_RECORDS :snprintf(exp, size, "WRITE RECORDS");break; + case ISO7816_APPEND_RECORD :snprintf(exp, size, "APPEND RECORD");break; + case ISO7816_UPDATE_RECORD :snprintf(exp, size, "UPDATE RECORD");break; + case ISO7816_GET_DATA :snprintf(exp, size, "GET DATA");break; + case ISO7816_PUT_DATA :snprintf(exp, size, "PUT DATA");break; + case ISO7816_SELECT_FILE :snprintf(exp, size, "SELECT FILE");break; + case ISO7816_VERIFY :snprintf(exp, size, "VERIFY");break; + case ISO7816_INTERNAL_AUTHENTICATION :snprintf(exp, size, "INTERNAL AUTH");break; + case ISO7816_EXTERNAL_AUTHENTICATION :snprintf(exp, size, "EXTERNAL AUTH");break; + case ISO7816_GET_CHALLENGE :snprintf(exp, size, "GET CHALLENGE");break; + case ISO7816_MANAGE_CHANNEL :snprintf(exp, size, "MANAGE CHANNEL");break; + default :snprintf(exp, size, "?"); break; + } + } +} + + +uint16_t printScTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace) { + // sanity check + if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) > traceLen) return traceLen; + + bool isResponse; + uint16_t data_len, parity_len; + uint32_t duration, timestamp, first_timestamp, EndOfTransmissionTimestamp; + char explanation[30] = {0}; + + first_timestamp = *((uint32_t *)(trace)); + timestamp = *((uint32_t *)(trace + tracepos)); + tracepos += 4; + + duration = *((uint16_t *)(trace + tracepos)); + tracepos += 2; + + data_len = *((uint16_t *)(trace + tracepos)); + tracepos += 2; + + if (data_len & 0x8000) { + data_len &= 0x7fff; + isResponse = true; + } else { + isResponse = false; + } + + parity_len = (data_len-1)/8 + 1; + if (tracepos + data_len + parity_len > traceLen) { + return traceLen; + } + uint8_t *frame = trace + tracepos; + tracepos += data_len; + //uint8_t *parityBytes = trace + tracepos; + tracepos += parity_len; + + //--- Draw the data column + char line[18][110]; + + if (data_len == 0 ) { + sprintf(line[0],""); + return tracepos; + } + + for (int j = 0; j < data_len && j/18 < 18; j++) { + snprintf(line[j/18]+(( j % 18) * 4),110, "%02x ", frame[j]); + } + + EndOfTransmissionTimestamp = timestamp + duration; + + annotateIso7816(explanation,sizeof(explanation),frame,data_len); + + int num_lines = MIN((data_len - 1)/18 + 1, 18); + for (int j = 0; j < num_lines ; j++) { + if (j == 0) { + PrintAndLog(" %10u | %10u | %s |%-72s | %s| %s", + (timestamp - first_timestamp), + (EndOfTransmissionTimestamp - first_timestamp), + (isResponse ? "Tag" : "Rdr"), + line[j], + " ", + (j == num_lines-1) ? explanation : ""); + } else { + PrintAndLog(" | | |%-72s | %s| %s", + line[j], + " ", + (j == num_lines-1) ? explanation : ""); + } + } + + // if is last record + if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) >= traceLen) return traceLen; + + return tracepos; +} + +int ScTraceList(const char *Cmd) { + bool loadFromFile = false; + bool saveToFile = false; + char type[5] = {0}; + char filename[FILE_PATH_SIZE] = {0}; + + // parse command line + param_getstr(Cmd, 0, type, sizeof(type)); + param_getstr(Cmd, 1, filename, sizeof(filename)); + + bool errors = false; + if(type[0] == 'h') { + errors = true; + } + + if(!errors) { + if (strcmp(type, "s") == 0) { + saveToFile = true; + } else if (strcmp(type,"l") == 0) { + loadFromFile = true; + } + } + + if ((loadFromFile || saveToFile) && strlen(filename) == 0) { + errors = true; + } + + if (loadFromFile && saveToFile) { + errors = true; + } + + if (errors) { + PrintAndLog("List or save protocol data."); + PrintAndLog("Usage: sc list [l ]"); + PrintAndLog(" sc list [s ]"); + PrintAndLog(" l - load data from file instead of trace buffer"); + PrintAndLog(" s - save data to file"); + PrintAndLog(""); + PrintAndLog("example: sc list"); + PrintAndLog("example: sc list save myCardTrace.trc"); + PrintAndLog("example: sc list l myCardTrace.trc"); + return 0; + } + + uint8_t *trace; + uint32_t tracepos = 0; + uint32_t traceLen = 0; + + if (loadFromFile) { + #define TRACE_CHUNK_SIZE (1<<16) // 64K to start with. Will be enough for BigBuf and some room for future extensions + FILE *tracefile = NULL; + size_t bytes_read; + trace = malloc(TRACE_CHUNK_SIZE); + if (trace == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + return 2; + } + if ((tracefile = fopen(filename,"rb")) == NULL) { + PrintAndLog("Could not open file %s", filename); + free(trace); + return 0; + } + while (!feof(tracefile)) { + bytes_read = fread(trace+traceLen, 1, TRACE_CHUNK_SIZE, tracefile); + traceLen += bytes_read; + if (!feof(tracefile)) { + uint8_t *p = realloc(trace, traceLen + TRACE_CHUNK_SIZE); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + fclose(tracefile); + return 2; + } + trace = p; + } + } + fclose(tracefile); + } else { + trace = malloc(USB_CMD_DATA_SIZE); + // Query for the size of the trace + UsbCommand response; + GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, -1, false); + traceLen = response.arg[2]; + if (traceLen > USB_CMD_DATA_SIZE) { + uint8_t *p = realloc(trace, traceLen); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + return 2; + } + trace = p; + GetFromBigBuf(trace, traceLen, 0, NULL, -1, false); + } + } + + if (saveToFile) { + FILE *tracefile = NULL; + if ((tracefile = fopen(filename,"wb")) == NULL) { + PrintAndLog("Could not create file %s", filename); + return 1; + } + fwrite(trace, 1, traceLen, tracefile); + PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, filename); + fclose(tracefile); + } else { + PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); + PrintAndLog(""); + PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); + PrintAndLog(""); + PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); + PrintAndLog("------------|------------|-----|-------------------------------------------------------------------------|-----|--------------------|"); + + while(tracepos < traceLen) + { + tracepos = printScTraceLine(tracepos, traceLen, trace); + } + } + + free(trace); + return 0; +} + +int CmdSmartList(const char *Cmd) { + ScTraceList(Cmd); + return 0; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"list", CmdSmartList, 0, "List ISO 7816 history"}, + {"info", CmdSmartInfo, 1, "Tag information [rdv40]"}, + {"reader", CmdSmartReader, 1, "Act like an IS07816 reader [rdv40]"}, + {"raw", CmdSmartRaw, 1, "Send raw hex data to tag [rdv40]"}, + {"upgrade", CmdSmartUpgrade, 1, "Upgrade firmware [rdv40]"}, + {"setclock",CmdSmartSetClock, 1, "Set clock speed"}, + {NULL, NULL, 0, NULL} +}; + +int CmdSmartcard(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdsmartcard.h b/client/cmdsmartcard.h new file mode 100644 index 00000000..caa06f4f --- /dev/null +++ b/client/cmdsmartcard.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 Smartcard module commands +//----------------------------------------------------------------------------- + +#ifndef CMDSMARTCARD_H__ +#define CMDSMARTCARD_H__ + +#include +#include +#include +#include +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "util.h" +#include "loclass/fileutils.h" // saveFile +#include "cmdmain.h" // getfromdevice +#include "emv/emvcore.h" // decodeTVL +#include "emv/apduinfo.h" // APDUcode description + +extern int CmdSmartcard(const char *Cmd); + +extern int CmdSmartRaw(const char* cmd); +extern int CmdSmartUpgrade(const char* cmd); +extern int CmdSmartInfo(const char* cmd); +extern int CmdSmartReader(const char *Cmd); + +extern int usage_sm_raw(void); +extern int usage_sm_reader(void); +extern int usage_sm_info(void); +extern int usage_sm_upgrade(void); +#endif diff --git a/client/comms.c b/client/comms.c new file mode 100644 index 00000000..190b9110 --- /dev/null +++ b/client/comms.c @@ -0,0 +1,436 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2009 Michael Gernoth +// Copyright (C) 2010 iZsh +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Code for communicating with the proxmark3 hardware. +//----------------------------------------------------------------------------- + +#include "comms.h" + +#include +#if defined(__linux__) && !defined(NO_UNLINK) +#include // for unlink() +#endif +#include "uart.h" +#include "ui.h" +#include "common.h" +#include "util_darwin.h" +#include "util_posix.h" + + +// Serial port that we are communicating with the PM3 on. +static serial_port sp = NULL; +static char *serial_port_name = NULL; + +// If TRUE, then there is no active connection to the PM3, and we will drop commands sent. +static bool offline; + +typedef struct { + bool run; // If TRUE, continue running the uart_communication thread + bool block_after_ACK; // if true, block after receiving an ACK package +} communication_arg_t; + +static communication_arg_t conn; +static pthread_t USB_communication_thread; + +// Transmit buffer. +static UsbCommand txBuffer; +static bool txBuffer_pending = false; +static pthread_mutex_t txBufferMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t txBufferSig = PTHREAD_COND_INITIALIZER; + +// Used by UsbReceiveCommand as a ring buffer for messages that are yet to be +// processed by a command handler (WaitForResponse{,Timeout}) +static UsbCommand rxBuffer[CMD_BUFFER_SIZE]; + +// Points to the next empty position to write to +static int cmd_head = 0; + +// Points to the position of the last unread command +static int cmd_tail = 0; + +// to lock rxBuffer operations from different threads +static pthread_mutex_t rxBufferMutex = PTHREAD_MUTEX_INITIALIZER; + +// These wrappers are required because it is not possible to access a static +// global variable outside of the context of a single file. + +void SetOffline(bool new_offline) { + offline = new_offline; +} + +bool IsOffline() { + return offline; +} + +void SendCommand(UsbCommand *c) { + #ifdef COMMS_DEBUG + printf("Sending %04x cmd\n", c->cmd); + #endif + + if (offline) { + PrintAndLog("Sending bytes to proxmark failed - offline"); + return; + } + + pthread_mutex_lock(&txBufferMutex); + /** + This causes hangups at times, when the pm3 unit is unresponsive or disconnected. The main console thread is alive, + but comm thread just spins here. Not good.../holiman + **/ + while (txBuffer_pending) { + pthread_cond_wait(&txBufferSig, &txBufferMutex); // wait for communication thread to complete sending a previous commmand + } + + txBuffer = *c; + txBuffer_pending = true; + pthread_cond_signal(&txBufferSig); // tell communication thread that a new command can be send + + pthread_mutex_unlock(&txBufferMutex); + +} + + +/** + * @brief This method should be called when sending a new command to the pm3. In case any old + * responses from previous commands are stored in the buffer, a call to this method should clear them. + * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which + * operation. Right now we'll just have to live with this. + */ +void clearCommandBuffer() +{ + //This is a very simple operation + pthread_mutex_lock(&rxBufferMutex); + cmd_tail = cmd_head; + pthread_mutex_unlock(&rxBufferMutex); +} + +/** + * @brief storeCommand stores a USB command in a circular buffer + * @param UC + */ +static void storeCommand(UsbCommand *command) +{ + pthread_mutex_lock(&rxBufferMutex); + if( (cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) + { + // If these two are equal, we're about to overwrite in the + // circular buffer. + PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); + } + + // Store the command at the 'head' location + UsbCommand* destination = &rxBuffer[cmd_head]; + memcpy(destination, command, sizeof(UsbCommand)); + + cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap + pthread_mutex_unlock(&rxBufferMutex); +} + + +/** + * @brief getCommand gets a command from an internal circular buffer. + * @param response location to write command + * @return 1 if response was returned, 0 if nothing has been received + */ +static int getCommand(UsbCommand* response) +{ + pthread_mutex_lock(&rxBufferMutex); + //If head == tail, there's nothing to read, or if we just got initialized + if (cmd_head == cmd_tail){ + pthread_mutex_unlock(&rxBufferMutex); + return 0; + } + + //Pick out the next unread command + UsbCommand* last_unread = &rxBuffer[cmd_tail]; + memcpy(response, last_unread, sizeof(UsbCommand)); + //Increment tail - this is a circular buffer, so modulo buffer size + cmd_tail = (cmd_tail + 1) % CMD_BUFFER_SIZE; + + pthread_mutex_unlock(&rxBufferMutex); + return 1; +} + + +//---------------------------------------------------------------------------------- +// Entry point into our code: called whenever we received a packet over USB. +// Handle debug commands directly, store all other commands in circular buffer. +//---------------------------------------------------------------------------------- +static void UsbCommandReceived(UsbCommand *UC) +{ + switch(UC->cmd) { + // First check if we are handling a debug message + case CMD_DEBUG_PRINT_STRING: { + char s[USB_CMD_DATA_SIZE+1]; + memset(s, 0x00, sizeof(s)); + size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); + memcpy(s,UC->d.asBytes,len); + PrintAndLog("#db# %s", s); + return; + } break; + + case CMD_DEBUG_PRINT_INTEGERS: { + PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); + return; + } break; + + default: + storeCommand(UC); + break; + } + +} + + +static void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*uart_communication(void *targ) { + communication_arg_t *conn = (communication_arg_t*)targ; + size_t rxlen; + UsbCommand rx; + UsbCommand *prx = ℞ + +#if defined(__MACH__) && defined(__APPLE__) + disableAppNap("Proxmark3 polling UART"); +#endif + + while (conn->run) { + rxlen = 0; + bool ACK_received = false; + if (uart_receive(sp, (uint8_t *)prx, sizeof(UsbCommand) - (prx-&rx), &rxlen) && rxlen) { + prx += rxlen; + if (prx-&rx < sizeof(UsbCommand)) { + continue; + } + UsbCommandReceived(&rx); + if (rx.cmd == CMD_ACK) { + ACK_received = true; + } + } + prx = ℞ + + + pthread_mutex_lock(&txBufferMutex); + + if (conn->block_after_ACK) { + // if we just received an ACK, wait here until a new command is to be transmitted + if (ACK_received) { + while (!txBuffer_pending) { + pthread_cond_wait(&txBufferSig, &txBufferMutex); + } + } + } + + if(txBuffer_pending) { + if (!uart_send(sp, (uint8_t*) &txBuffer, sizeof(UsbCommand))) { + PrintAndLog("Sending bytes to proxmark failed"); + } + txBuffer_pending = false; + pthread_cond_signal(&txBufferSig); // tell main thread that txBuffer is empty + } + + pthread_mutex_unlock(&txBufferMutex); + } + +#if defined(__MACH__) && defined(__APPLE__) + enableAppNap(); +#endif + + pthread_exit(NULL); + return NULL; +} + + +/** + * Data transfer from Proxmark to client. This method times out after + * ms_timeout milliseconds. + * @brief GetFromBigBuf + * @param dest Destination address for transfer + * @param bytes number of bytes to be transferred + * @param start_index offset into Proxmark3 BigBuf[] + * @param response struct to copy last command (CMD_ACK) into + * @param ms_timeout timeout in milliseconds + * @param show_warning display message after 2 seconds + * @return true if command was returned, otherwise false + */ +bool GetFromBigBuf(uint8_t *dest, int bytes, int start_index, UsbCommand *response, size_t ms_timeout, bool show_warning) +{ + UsbCommand c = {CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K, {start_index, bytes, 0}}; + SendCommand(&c); + + uint64_t start_time = msclock(); + + UsbCommand resp; + if (response == NULL) { + response = &resp; + } + + int bytes_completed = 0; + while(true) { + if (getCommand(response)) { + if (response->cmd == CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) { + int copy_bytes = MIN(bytes - bytes_completed, response->arg[1]); + memcpy(dest + response->arg[0], response->d.asBytes, copy_bytes); + bytes_completed += copy_bytes; + } else if (response->cmd == CMD_ACK) { + return true; + } + } + + if (msclock() - start_time > ms_timeout) { + break; + } + + if (msclock() - start_time > 2000 && show_warning) { + PrintAndLog("Waiting for a response from the proxmark..."); + PrintAndLog("You can cancel this operation by pressing the pm3 button"); + show_warning = false; + } + } + + return false; +} + + +bool OpenProxmark(void *port, bool wait_for_port, int timeout, bool flash_mode) { + char *portname = (char *)port; + if (!wait_for_port) { + sp = uart_open(portname); + } else { + printf("Waiting for Proxmark to appear on %s ", portname); + fflush(stdout); + int openCount = 0; + do { + sp = uart_open(portname); + msleep(1000); + printf("."); + fflush(stdout); + } while(++openCount < timeout && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT)); + printf("\n"); + } + + // check result of uart opening + if (sp == INVALID_SERIAL_PORT) { + printf("ERROR: invalid serial port\n"); + sp = NULL; + serial_port_name = NULL; + return false; + } else if (sp == CLAIMED_SERIAL_PORT) { + printf("ERROR: serial port is claimed by another process\n"); + sp = NULL; + serial_port_name = NULL; + return false; + } else { + // start the USB communication thread + serial_port_name = portname; + conn.run = true; + conn.block_after_ACK = flash_mode; + pthread_create(&USB_communication_thread, NULL, &uart_communication, &conn); + return true; + } +} + + +void CloseProxmark(void) { + conn.run = false; + +#ifdef __BIONIC__ + // In Android O and later, if an invalid pthread_t is passed to pthread_join, it calls fatal(). + // https://github.com/aosp-mirror/platform_bionic/blob/ed16b344e75f422fb36fbfd91fb30de339475880/libc/bionic/pthread_internal.cpp#L116-L128 + // + // In Bionic libc, pthread_t is an integer. + + if (USB_communication_thread != 0) { + pthread_join(USB_communication_thread, NULL); + } +#else + // pthread_t is a struct on other libc, treat as an opaque memory reference + pthread_join(USB_communication_thread, NULL); +#endif + + if (sp) { + uart_close(sp); + } + +#if defined(__linux__) && !defined(NO_UNLINK) + // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* + // + // This may be disabled at compile-time with -DNO_UNLINK (used for a JNI-based serial port on Android). + if (serial_port_name) { + unlink(serial_port_name); + } +#endif + + // Clean up our state + sp = NULL; + serial_port_name = NULL; +#ifdef __BIONIC__ + memset(&USB_communication_thread, 0, sizeof(pthread_t)); +#endif +} + + +/** + * Waits for a certain response type. This method waits for a maximum of + * ms_timeout milliseconds for a specified response command. + *@brief WaitForResponseTimeout + * @param cmd command to wait for, or CMD_UNKNOWN to take any command. + * @param response struct to copy received command into. + * @param ms_timeout + * @param show_warning display message after 2 seconds + * @return true if command was returned, otherwise false + */ +bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { + + UsbCommand resp; + + #ifdef COMMS_DEBUG + printf("Waiting for %04x cmd\n", cmd); + #endif + + if (response == NULL) { + response = &resp; + } + + uint64_t start_time = msclock(); + + // Wait until the command is received + while (true) { + while(getCommand(response)) { + if (cmd == CMD_UNKNOWN || response->cmd == cmd) { + return true; + } + } + + if (msclock() - start_time > ms_timeout) { + break; + } + + if (msclock() - start_time > 2000 && show_warning) { + // 2 seconds elapsed (but this doesn't mean the timeout was exceeded) + PrintAndLog("Waiting for a response from the proxmark..."); + PrintAndLog("You can cancel this operation by pressing the pm3 button"); + show_warning = false; + } + } + return false; +} + + +bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { + return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); +} + +bool WaitForResponse(uint32_t cmd, UsbCommand* response) { + return WaitForResponseTimeoutW(cmd, response, -1, true); +} + diff --git a/client/comms.h b/client/comms.h new file mode 100644 index 00000000..68981165 --- /dev/null +++ b/client/comms.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2009 Michael Gernoth +// Copyright (C) 2010 iZsh +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Code for communicating with the proxmark3 hardware. +//----------------------------------------------------------------------------- + +#ifndef COMMS_H_ +#define COMMS_H_ + +#include +#include + +#include "usb_cmd.h" +#include "uart.h" + +#ifndef CMD_BUFFER_SIZE +#define CMD_BUFFER_SIZE 50 +#endif + +void SetOffline(bool new_offline); +bool IsOffline(); + +bool OpenProxmark(void *port, bool wait_for_port, int timeout, bool flash_mode); +void CloseProxmark(void); + +void SendCommand(UsbCommand *c); + +void clearCommandBuffer(); +bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); +bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); +bool WaitForResponse(uint32_t cmd, UsbCommand* response); +bool GetFromBigBuf(uint8_t *dest, int bytes, int start_index, UsbCommand *response, size_t ms_timeout, bool show_warning); + +#endif // COMMS_H_ diff --git a/client/emv/apduinfo.c b/client/emv/apduinfo.c new file mode 100644 index 00000000..d14e3ff6 --- /dev/null +++ b/client/emv/apduinfo.c @@ -0,0 +1,317 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// APDU status bytes information +//----------------------------------------------------------------------------- + +#include "apduinfo.h" + +const APDUCode APDUCodeTable[] = { + // ID Type Description + {"XXXX", APDUCODE_TYPE_NONE, ""}, // blank string + {"6---", APDUCODE_TYPE_ERROR, "Class not supported."}, + {"61--", APDUCODE_TYPE_INFO, "Response bytes still available"}, + {"61XX", APDUCODE_TYPE_INFO, "Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE."}, + {"62--", APDUCODE_TYPE_WARNING, "State of non-volatile memory unchanged"}, + {"6200", APDUCODE_TYPE_WARNING, "No information given (NV-Ram not changed)"}, + {"6201", APDUCODE_TYPE_WARNING, "NV-Ram not changed 1."}, + {"6281", APDUCODE_TYPE_WARNING, "Part of returned data may be corrupted"}, + {"6282", APDUCODE_TYPE_WARNING, "End of file/record reached before reading Le bytes"}, + {"6283", APDUCODE_TYPE_WARNING, "Selected file invalidated"}, + {"6284", APDUCODE_TYPE_WARNING, "Selected file is not valid. FCI not formated according to ISO"}, + {"6285", APDUCODE_TYPE_WARNING, "No input data available from a sensor on the card. No Purse Engine enslaved for R3bc"}, + {"62A2", APDUCODE_TYPE_WARNING, "Wrong R-MAC"}, + {"62A4", APDUCODE_TYPE_WARNING, "Card locked (during reset( ))"}, + {"62CX", APDUCODE_TYPE_WARNING, "Counter with value x (command dependent)"}, + {"62F1", APDUCODE_TYPE_WARNING, "Wrong C-MAC"}, + {"62F3", APDUCODE_TYPE_WARNING, "Internal reset"}, + {"62F5", APDUCODE_TYPE_WARNING, "Default agent locked"}, + {"62F7", APDUCODE_TYPE_WARNING, "Cardholder locked"}, + {"62F8", APDUCODE_TYPE_WARNING, "Basement is current agent"}, + {"62F9", APDUCODE_TYPE_WARNING, "CALC Key Set not unblocked"}, + {"62FX", APDUCODE_TYPE_WARNING, "-"}, + {"62XX", APDUCODE_TYPE_WARNING, "RFU"}, + {"63--", APDUCODE_TYPE_WARNING, "State of non-volatile memory changed"}, + {"6300", APDUCODE_TYPE_WARNING, "No information given (NV-Ram changed)"}, + {"6381", APDUCODE_TYPE_WARNING, "File filled up by the last write. Loading/updating is not allowed."}, + {"6382", APDUCODE_TYPE_WARNING, "Card key not supported."}, + {"6383", APDUCODE_TYPE_WARNING, "Reader key not supported."}, + {"6384", APDUCODE_TYPE_WARNING, "Plaintext transmission not supported."}, + {"6385", APDUCODE_TYPE_WARNING, "Secured transmission not supported."}, + {"6386", APDUCODE_TYPE_WARNING, "Volatile memory is not available."}, + {"6387", APDUCODE_TYPE_WARNING, "Non-volatile memory is not available."}, + {"6388", APDUCODE_TYPE_WARNING, "Key number not valid."}, + {"6389", APDUCODE_TYPE_WARNING, "Key length is not correct."}, + {"63C0", APDUCODE_TYPE_WARNING, "Verify fail, no try left."}, + {"63C1", APDUCODE_TYPE_WARNING, "Verify fail, 1 try left."}, + {"63C2", APDUCODE_TYPE_WARNING, "Verify fail, 2 tries left."}, + {"63C3", APDUCODE_TYPE_WARNING, "Verify fail, 3 tries left."}, + {"63CX", APDUCODE_TYPE_WARNING, "The counter has reached the value 'x' (0 = x = 15) (command dependent)."}, + {"63F1", APDUCODE_TYPE_WARNING, "More data expected."}, + {"63F2", APDUCODE_TYPE_WARNING, "More data expected and proactive command pending."}, + {"63FX", APDUCODE_TYPE_WARNING, "-"}, + {"63XX", APDUCODE_TYPE_WARNING, "RFU"}, + {"64--", APDUCODE_TYPE_ERROR, "State of non-volatile memory unchanged"}, + {"6400", APDUCODE_TYPE_ERROR, "No information given (NV-Ram not changed)"}, + {"6401", APDUCODE_TYPE_ERROR, "Command timeout. Immediate response required by the card."}, + {"64XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"65--", APDUCODE_TYPE_ERROR, "State of non-volatile memory changed"}, + {"6500", APDUCODE_TYPE_ERROR, "No information given"}, + {"6501", APDUCODE_TYPE_ERROR, "Write error. Memory failure. There have been problems in writing or reading the EEPROM. Other hardware problems may also bring this error."}, + {"6581", APDUCODE_TYPE_ERROR, "Memory failure"}, + {"65FX", APDUCODE_TYPE_ERROR, "-"}, + {"65XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"66--", APDUCODE_TYPE_SECURITY, " "}, + {"6600", APDUCODE_TYPE_SECURITY, "Error while receiving (timeout)"}, + {"6601", APDUCODE_TYPE_SECURITY, "Error while receiving (character parity error)"}, + {"6602", APDUCODE_TYPE_SECURITY, "Wrong checksum"}, + {"6603", APDUCODE_TYPE_SECURITY, "The current DF file without FCI"}, + {"6604", APDUCODE_TYPE_SECURITY, "No SF or KF under the current DF"}, + {"6669", APDUCODE_TYPE_SECURITY, "Incorrect Encryption/Decryption Padding"}, + {"66XX", APDUCODE_TYPE_SECURITY, "-"}, + {"67--", APDUCODE_TYPE_ERROR, " "}, + {"6700", APDUCODE_TYPE_ERROR, "Wrong length"}, + {"67XX", APDUCODE_TYPE_ERROR, "length incorrect (procedure)(ISO 7816-3)"}, + {"68--", APDUCODE_TYPE_ERROR, "Functions in CLA not supported"}, + {"6800", APDUCODE_TYPE_ERROR, "No information given (The request function is not supported by the card)"}, + {"6881", APDUCODE_TYPE_ERROR, "Logical channel not supported"}, + {"6882", APDUCODE_TYPE_ERROR, "Secure messaging not supported"}, + {"6883", APDUCODE_TYPE_ERROR, "Last command of the chain expected"}, + {"6884", APDUCODE_TYPE_ERROR, "Command chaining not supported"}, + {"68FX", APDUCODE_TYPE_ERROR, "-"}, + {"68XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"69--", APDUCODE_TYPE_ERROR, "Command not allowed"}, + {"6900", APDUCODE_TYPE_ERROR, "No information given (Command not allowed)"}, + {"6901", APDUCODE_TYPE_ERROR, "Command not accepted (inactive state)"}, + {"6981", APDUCODE_TYPE_ERROR, "Command incompatible with file structure"}, + {"6982", APDUCODE_TYPE_ERROR, "Security condition not satisfied."}, + {"6983", APDUCODE_TYPE_ERROR, "Authentication method blocked"}, + {"6984", APDUCODE_TYPE_ERROR, "Referenced data reversibly blocked (invalidated)"}, + {"6985", APDUCODE_TYPE_ERROR, "Conditions of use not satisfied."}, + {"6986", APDUCODE_TYPE_ERROR, "Command not allowed (no current EF)"}, + {"6987", APDUCODE_TYPE_ERROR, "Expected secure messaging (SM) object missing"}, + {"6988", APDUCODE_TYPE_ERROR, "Incorrect secure messaging (SM) data object"}, + {"698D", APDUCODE_TYPE_NONE, "Reserved"}, + {"6996", APDUCODE_TYPE_ERROR, "Data must be updated again"}, + {"69E1", APDUCODE_TYPE_ERROR, "POL1 of the currently Enabled Profile prevents this action."}, + {"69F0", APDUCODE_TYPE_ERROR, "Permission Denied"}, + {"69F1", APDUCODE_TYPE_ERROR, "Permission Denied - Missing Privilege"}, + {"69FX", APDUCODE_TYPE_ERROR, "-"}, + {"69XX", APDUCODE_TYPE_ERROR, "RFU"}, + {"6A--", APDUCODE_TYPE_ERROR, "Wrong parameter(s) P1-P2"}, + {"6A00", APDUCODE_TYPE_ERROR, "No information given (Bytes P1 and/or P2 are incorrect)"}, + {"6A80", APDUCODE_TYPE_ERROR, "The parameters in the data field are incorrect."}, + {"6A81", APDUCODE_TYPE_ERROR, "Function not supported"}, + {"6A82", APDUCODE_TYPE_ERROR, "File not found"}, + {"6A83", APDUCODE_TYPE_ERROR, "Record not found"}, + {"6A84", APDUCODE_TYPE_ERROR, "There is insufficient memory space in record or file"}, + {"6A85", APDUCODE_TYPE_ERROR, "Lc inconsistent with TLV structure"}, + {"6A86", APDUCODE_TYPE_ERROR, "Incorrect P1 or P2 parameter."}, + {"6A87", APDUCODE_TYPE_ERROR, "Lc inconsistent with P1-P2"}, + {"6A88", APDUCODE_TYPE_ERROR, "Referenced data not found"}, + {"6A89", APDUCODE_TYPE_ERROR, "File already exists"}, + {"6A8A", APDUCODE_TYPE_ERROR, "DF name already exists."}, + {"6AF0", APDUCODE_TYPE_ERROR, "Wrong parameter value"}, + {"6AFX", APDUCODE_TYPE_ERROR, "-"}, + {"6AXX", APDUCODE_TYPE_ERROR, "RFU"}, + {"6B--", APDUCODE_TYPE_ERROR, " "}, + {"6B00", APDUCODE_TYPE_ERROR, "Wrong parameter(s) P1-P2"}, + {"6BXX", APDUCODE_TYPE_ERROR, "Reference incorrect (procedure byte), (ISO 7816-3)"}, + {"6C--", APDUCODE_TYPE_ERROR, "Wrong length Le"}, + {"6C00", APDUCODE_TYPE_ERROR, "Incorrect P3 length."}, + {"6CXX", APDUCODE_TYPE_ERROR, "Bad length value in Le; 'xx' is the correct exact Le"}, + {"6D--", APDUCODE_TYPE_ERROR, " "}, + {"6D00", APDUCODE_TYPE_ERROR, "Instruction code not supported or invalid"}, + {"6DXX", APDUCODE_TYPE_ERROR, "Instruction code not programmed or invalid (procedure byte), (ISO 7816-3)"}, + {"6E--", APDUCODE_TYPE_ERROR, " "}, + {"6E00", APDUCODE_TYPE_ERROR, "Class not supported"}, + {"6EXX", APDUCODE_TYPE_ERROR, "Instruction class not supported (procedure byte), (ISO 7816-3)"}, + {"6F--", APDUCODE_TYPE_ERROR, "Internal exception"}, + {"6F00", APDUCODE_TYPE_ERROR, "Command aborted - more exact diagnosis not possible (e.g., operating system error)."}, + {"6FFF", APDUCODE_TYPE_ERROR, "Card dead (overuse, ...)"}, + {"6FXX", APDUCODE_TYPE_ERROR, "No precise diagnosis (procedure byte), (ISO 7816-3)"}, + {"9---", APDUCODE_TYPE_NONE, ""}, + {"9000", APDUCODE_TYPE_INFO, "Command successfully executed (OK)."}, + {"9004", APDUCODE_TYPE_WARNING, "PIN not succesfully verified, 3 or more PIN tries left"}, + {"9008", APDUCODE_TYPE_NONE, "Key/file not found"}, + {"9080", APDUCODE_TYPE_WARNING, "Unblock Try Counter has reached zero"}, + {"9100", APDUCODE_TYPE_NONE, "OK"}, + {"9101", APDUCODE_TYPE_NONE, "States.activity, States.lock Status or States.lockable has wrong value"}, + {"9102", APDUCODE_TYPE_NONE, "Transaction number reached its limit"}, + {"910C", APDUCODE_TYPE_NONE, "No changes"}, + {"910E", APDUCODE_TYPE_NONE, "Insufficient NV-Memory to complete command"}, + {"911C", APDUCODE_TYPE_NONE, "Command code not supported"}, + {"911E", APDUCODE_TYPE_NONE, "CRC or MAC does not match data"}, + {"9140", APDUCODE_TYPE_NONE, "Invalid key number specified"}, + {"917E", APDUCODE_TYPE_NONE, "Length of command string invalid"}, + {"919D", APDUCODE_TYPE_NONE, "Not allow the requested command"}, + {"919E", APDUCODE_TYPE_NONE, "Value of the parameter invalid"}, + {"91A0", APDUCODE_TYPE_NONE, "Requested AID not present on PICC"}, + {"91A1", APDUCODE_TYPE_NONE, "Unrecoverable error within application"}, + {"91AE", APDUCODE_TYPE_NONE, "Authentication status does not allow the requested command"}, + {"91AF", APDUCODE_TYPE_NONE, "Additional data frame is expected to be sent"}, + {"91BE", APDUCODE_TYPE_NONE, "Out of boundary"}, + {"91C1", APDUCODE_TYPE_NONE, "Unrecoverable error within PICC"}, + {"91CA", APDUCODE_TYPE_NONE, "Previous Command was not fully completed"}, + {"91CD", APDUCODE_TYPE_NONE, "PICC was disabled by an unrecoverable error"}, + {"91CE", APDUCODE_TYPE_NONE, "Number of Applications limited to 28"}, + {"91DE", APDUCODE_TYPE_NONE, "File or application already exists"}, + {"91EE", APDUCODE_TYPE_NONE, "Could not complete NV-write operation due to loss of power"}, + {"91F0", APDUCODE_TYPE_NONE, "Specified file number does not exist"}, + {"91F1", APDUCODE_TYPE_NONE, "Unrecoverable error within file"}, + {"920x", APDUCODE_TYPE_INFO, "Writing to EEPROM successful after 'x' attempts."}, + {"9210", APDUCODE_TYPE_ERROR, "Insufficient memory. No more storage available."}, + {"9240", APDUCODE_TYPE_ERROR, "Writing to EEPROM not successful."}, + {"9301", APDUCODE_TYPE_NONE, "Integrity error"}, + {"9302", APDUCODE_TYPE_NONE, "Candidate S2 invalid"}, + {"9303", APDUCODE_TYPE_ERROR, "Application is permanently locked"}, + {"9400", APDUCODE_TYPE_ERROR, "No EF selected."}, + {"9401", APDUCODE_TYPE_NONE, "Candidate currency code does not match purse currency"}, + {"9402", APDUCODE_TYPE_NONE, "Candidate amount too high"}, + {"9402", APDUCODE_TYPE_ERROR, "Address range exceeded."}, + {"9403", APDUCODE_TYPE_NONE, "Candidate amount too low"}, + {"9404", APDUCODE_TYPE_ERROR, "FID not found, record not found or comparison pattern not found."}, + {"9405", APDUCODE_TYPE_NONE, "Problems in the data field"}, + {"9406", APDUCODE_TYPE_ERROR, "Required MAC unavailable"}, + {"9407", APDUCODE_TYPE_NONE, "Bad currency : purse engine has no slot with R3bc currency"}, + {"9408", APDUCODE_TYPE_NONE, "R3bc currency not supported in purse engine"}, + {"9408", APDUCODE_TYPE_ERROR, "Selected file type does not match command."}, + {"9580", APDUCODE_TYPE_NONE, "Bad sequence"}, + {"9681", APDUCODE_TYPE_NONE, "Slave not found"}, + {"9700", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 1 or 2"}, + {"9702", APDUCODE_TYPE_NONE, "Main keys are blocked"}, + {"9704", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 3 or more PIN tries left"}, + {"9784", APDUCODE_TYPE_NONE, "Base key"}, + {"9785", APDUCODE_TYPE_NONE, "Limit exceeded - C-MAC key"}, + {"9786", APDUCODE_TYPE_NONE, "SM error - Limit exceeded - R-MAC key"}, + {"9787", APDUCODE_TYPE_NONE, "Limit exceeded - sequence counter"}, + {"9788", APDUCODE_TYPE_NONE, "Limit exceeded - R-MAC length"}, + {"9789", APDUCODE_TYPE_NONE, "Service not available"}, + {"9802", APDUCODE_TYPE_ERROR, "No PIN defined."}, + {"9804", APDUCODE_TYPE_ERROR, "Access conditions not satisfied, authentication failed."}, + {"9835", APDUCODE_TYPE_ERROR, "ASK RANDOM or GIVE RANDOM not executed."}, + {"9840", APDUCODE_TYPE_ERROR, "PIN verification not successful."}, + {"9850", APDUCODE_TYPE_ERROR, "INCREASE or DECREASE could not be executed because a limit has been reached."}, + {"9862", APDUCODE_TYPE_ERROR, "Authentication Error, application specific (incorrect MAC)"}, + {"9900", APDUCODE_TYPE_NONE, "1 PIN try left"}, + {"9904", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 1 PIN try left"}, + {"9985", APDUCODE_TYPE_NONE, "Wrong status - Cardholder lock"}, + {"9986", APDUCODE_TYPE_ERROR, "Missing privilege"}, + {"9987", APDUCODE_TYPE_NONE, "PIN is not installed"}, + {"9988", APDUCODE_TYPE_NONE, "Wrong status - R-MAC state"}, + {"9A00", APDUCODE_TYPE_NONE, "2 PIN try left"}, + {"9A04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, 2 PIN try left"}, + {"9A71", APDUCODE_TYPE_NONE, "Wrong parameter value - Double agent AID"}, + {"9A72", APDUCODE_TYPE_NONE, "Wrong parameter value - Double agent Type"}, + {"9D05", APDUCODE_TYPE_ERROR, "Incorrect certificate type"}, + {"9D07", APDUCODE_TYPE_ERROR, "Incorrect session data size"}, + {"9D08", APDUCODE_TYPE_ERROR, "Incorrect DIR file record size"}, + {"9D09", APDUCODE_TYPE_ERROR, "Incorrect FCI record size"}, + {"9D0A", APDUCODE_TYPE_ERROR, "Incorrect code size"}, + {"9D10", APDUCODE_TYPE_ERROR, "Insufficient memory to load application"}, + {"9D11", APDUCODE_TYPE_ERROR, "Invalid AID"}, + {"9D12", APDUCODE_TYPE_ERROR, "Duplicate AID"}, + {"9D13", APDUCODE_TYPE_ERROR, "Application previously loaded"}, + {"9D14", APDUCODE_TYPE_ERROR, "Application history list full"}, + {"9D15", APDUCODE_TYPE_ERROR, "Application not open"}, + {"9D17", APDUCODE_TYPE_ERROR, "Invalid offset"}, + {"9D18", APDUCODE_TYPE_ERROR, "Application already loaded"}, + {"9D19", APDUCODE_TYPE_ERROR, "Invalid certificate"}, + {"9D1A", APDUCODE_TYPE_ERROR, "Invalid signature"}, + {"9D1B", APDUCODE_TYPE_ERROR, "Invalid KTU"}, + {"9D1D", APDUCODE_TYPE_ERROR, "MSM controls not set"}, + {"9D1E", APDUCODE_TYPE_ERROR, "Application signature does not exist"}, + {"9D1F", APDUCODE_TYPE_ERROR, "KTU does not exist"}, + {"9D20", APDUCODE_TYPE_ERROR, "Application not loaded"}, + {"9D21", APDUCODE_TYPE_ERROR, "Invalid Open command data length"}, + {"9D30", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (invalid start address)"}, + {"9D31", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (invalid length)"}, + {"9D32", APDUCODE_TYPE_ERROR, "Check data parameter is incorrect (illegal memory check area)"}, + {"9D40", APDUCODE_TYPE_ERROR, "Invalid MSM Controls ciphertext"}, + {"9D41", APDUCODE_TYPE_ERROR, "MSM controls already set"}, + {"9D42", APDUCODE_TYPE_ERROR, "Set MSM Controls data length less than 2 bytes"}, + {"9D43", APDUCODE_TYPE_ERROR, "Invalid MSM Controls data length"}, + {"9D44", APDUCODE_TYPE_ERROR, "Excess MSM Controls ciphertext"}, + {"9D45", APDUCODE_TYPE_ERROR, "Verification of MSM Controls data failed"}, + {"9D50", APDUCODE_TYPE_ERROR, "Invalid MCD Issuer production ID"}, + {"9D51", APDUCODE_TYPE_ERROR, "Invalid MCD Issuer ID"}, + {"9D52", APDUCODE_TYPE_ERROR, "Invalid set MSM controls data date"}, + {"9D53", APDUCODE_TYPE_ERROR, "Invalid MCD number"}, + {"9D54", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D55", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D56", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D57", APDUCODE_TYPE_ERROR, "Reserved field error"}, + {"9D60", APDUCODE_TYPE_ERROR, "MAC verification failed"}, + {"9D61", APDUCODE_TYPE_ERROR, "Maximum number of unblocks reached"}, + {"9D62", APDUCODE_TYPE_ERROR, "Card was not blocked"}, + {"9D63", APDUCODE_TYPE_ERROR, "Crypto functions not available"}, + {"9D64", APDUCODE_TYPE_ERROR, "No application loaded"}, + {"9E00", APDUCODE_TYPE_NONE, "PIN not installed"}, + {"9E04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, PIN not installed"}, + {"9F00", APDUCODE_TYPE_NONE, "PIN blocked and Unblock Try Counter is 3"}, + {"9F04", APDUCODE_TYPE_NONE, "PIN not succesfully verified, PIN blocked and Unblock Try Counter is 3"}, + {"9FXX", APDUCODE_TYPE_NONE, "Command successfully executed; 'xx' bytes of data are available and can be requested using GET RESPONSE."}, + {"9XXX", APDUCODE_TYPE_NONE, "Application related status, (ISO 7816-3)"} +}; +const size_t APDUCodeTableLen = sizeof(APDUCodeTable)/sizeof(APDUCode); + +int CodeCmp(const char *code1, const char *code2) { + int xsymb = 0; + int cmp = 0; + for (int i = 0; i < 4; i++) { + if (code1[i] == code2[i]) + cmp++; + if (code1[i] == 'X' || code2[i] == 'X') + xsymb++; + } + if (cmp == 4) + return 0; + + if (cmp + xsymb == 4) + return xsymb; + + return -1; +} + +const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2) { + char buf[5] = {0}; + int res; + int mineq = APDUCodeTableLen; + int mineqindx = 0; + + sprintf(buf, "%02X%02X", sw1, sw2); + + for (int i = 0; i < APDUCodeTableLen; i++) { + res = CodeCmp(APDUCodeTable[i].ID, buf); + + // equal + if (res == 0) { + return &APDUCodeTable[i]; + } + + // with some 'X' + if (res > 0 && mineq > res) { + mineq = res; + mineqindx = i; + } + } + + // if we have not equal, but with some 'X' + if (mineqindx < APDUCodeTableLen) { + return &APDUCodeTable[mineqindx]; + } + + return NULL; +} + +const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2) { + const APDUCode *cd = GetAPDUCode(sw1, sw2); + if (cd) + return cd->Description; + else + return APDUCodeTable[0].Description; //empty string +} diff --git a/client/emv/apduinfo.h b/client/emv/apduinfo.h new file mode 100644 index 00000000..a3fa2049 --- /dev/null +++ b/client/emv/apduinfo.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// APDU status bytes information +//----------------------------------------------------------------------------- + +#ifndef APDUINFO_H__ +#define APDUINFO_H__ + +#include +#include +#include +#include + +#define APDUCODE_TYPE_NONE 0 +#define APDUCODE_TYPE_INFO 1 +#define APDUCODE_TYPE_WARNING 2 +#define APDUCODE_TYPE_ERROR 3 +#define APDUCODE_TYPE_SECURITY 4 + +typedef struct { + const char *ID; + const uint8_t Type; + const char *Description; +} APDUCode; + +extern const APDUCode* const GetAPDUCode(uint8_t sw1, uint8_t sw2); +extern const char* GetAPDUCodeDescription(uint8_t sw1, uint8_t sw2); + +#endif diff --git a/client/emv/capk.txt b/client/emv/capk.txt new file mode 100644 index 00000000..0925fbad --- /dev/null +++ b/client/emv/capk.txt @@ -0,0 +1,32 @@ +a0:00:00:00:03 01 091231 rsa 03 c6:96:03:42:13:d7:d8:54:69:84:57:9d:1d:0f:0e:a5:19:cf:f8:de:ff:c4:29:35:4c:f3:a8:71:a6:f7:18:3f:12:28:da:5c:74:70:c0:55:38:71:00:cb:93:5a:71:2c:4e:28:64:df:5d:64:ba:93:fe:7e:63:e7:1f:25:b1:e5:f5:29:85:75:eb:e1:c6:3a:a6:17:70:69:17:91:1d:c2:a7:5a:c2:8b:25:1c:7e:f4:0f:23:65:91:24:90:b9:39:bc:a2:12:4a:30:a2:8f:54:40:2c:34:ae:ca:33:1a:b6:7e:1e:79:b2:85:dd:57:71:b5:d9:ff:79:ea:63:0b:75 sha1 d3:4a:6a:77:60:11:c7:e7:ce:3a:ec:5f:03:ad:2f:8c:fc:55:03:cc +a0:00:00:00:03 07 171231 rsa 03 a8:9f:25:a5:6f:a6:da:25:8c:8c:a8:b4:04:27:d9:27:b4:a1:eb:4d:7e:a3:26:bb:b1:2f:97:de:d7:0a:e5:e4:48:0f:c9:c5:e8:a9:72:17:71:10:a1:cc:31:8d:06:d2:f8:f5:c4:84:4a:c5:fa:79:a4:dc:47:0b:b1:1e:d6:35:69:9c:17:08:1b:90:f1:b9:84:f1:2e:92:c1:c5:29:27:6d:8a:f8:ec:7f:28:49:20:97:d8:cd:5b:ec:ea:16:fe:40:88:f6:cf:ab:4a:1b:42:32:8a:1b:99:6f:92:78:b0:b7:e3:31:1c:a5:ef:85:6c:2f:88:84:74:b8:36:12:a8:2e:4e:00:d0:cd:40:69:a6:78:31:40:43:3d:50:72:5f sha1 b4:bc:56:cc:4e:88:32:49:32:cb:c6:43:d6:89:8f:6f:e5:93:b1:72 +a0:00:00:00:03 08 221231 rsa 03 d9:fd:6e:d7:5d:51:d0:e3:06:64:bd:15:70:23:ea:a1:ff:a8:71:e4:da:65:67:2b:86:3d:25:5e:81:e1:37:a5:1d:e4:f7:2b:cc:9e:44:ac:e1:21:27:f8:7e:26:3d:3a:f9:dd:9c:f3:5c:a4:a7:b0:1e:90:70:00:ba:85:d2:49:54:c2:fc:a3:07:48:25:dd:d4:c0:c8:f1:86:cb:02:0f:68:3e:02:f2:de:ad:39:69:13:3f:06:f7:84:51:66:ac:eb:57:ca:0f:c2:60:34:45:46:98:11:d2:93:bf:ef:ba:fa:b5:76:31:b3:dd:91:e7:96:bf:85:0a:25:01:2f:1a:e3:8f:05:aa:5c:4d:6d:03:b1:dc:2e:56:86:12:78:59:38:bb:c9:b3:cd:3a:91:0c:1d:a5:5a:5a:92:18:ac:e0:f7:a2:12:87:75:26:82:f1:58:32:a6:78:d6:e1:ed:0b sha1 20:d2:13:12:69:55:de:20:5a:dc:2f:d2:82:2b:d2:2d:e2:1c:f9:a8 +a0:00:00:00:03 09 221231 rsa 03 9d:91:22:48:de:0a:4e:39:c1:a7:dd:e3:f6:d2:58:89:92:c1:a4:09:5a:fb:d1:82:4d:1b:a7:48:47:f2:bc:49:26:d2:ef:d9:04:b4:b5:49:54:cd:18:9a:54:c5:d1:17:96:54:f8:f9:b0:d2:ab:5f:03:57:eb:64:2f:ed:a9:5d:39:12:c6:57:69:45:fa:b8:97:e7:06:2c:aa:44:a4:aa:06:b8:fe:6e:3d:ba:18:af:6a:e3:73:8e:30:42:9e:e9:be:03:42:7c:9d:64:f6:95:fa:8c:ab:4b:fe:37:68:53:ea:34:ad:1d:76:bf:ca:d1:59:08:c0:77:ff:e6:dc:55:21:ec:ef:5d:27:8a:96:e2:6f:57:35:9f:fa:ed:a1:94:34:b9:37:f1:ad:99:9d:c5:c4:1e:b1:19:35:b4:4c:18:10:0e:85:7f:43:1a:4a:5a:6b:b6:51:14:f1:74:c2:d7:b5:9f:df:23:7d:6b:b1:dd:09:16:e6:44:d7:09:de:d5:64:81:47:7c:75:d9:5c:dd:68:25:46:15:f7:74:0e:c0:7f:33:0a:c5:d6:7b:cd:75:bf:23:d2:8a:14:08:26:c0:26:db:de:97:1a:37:cd:3e:f9:b8:df:64:4a:c3:85:01:05:01:ef:c6:50:9d:7a:41 sha1 1f:f8:0a:40:17:3f:52:d7:d2:7e:0f:26:a1:46:a1:c8:cc:b2:90:46 +a0:00:00:00:03 95 000000 rsa 03 be:9e:1f:a5:e9:a8:03:85:29:99:c4:ab:43:2d:b2:86:00:dc:d9:da:b7:6d:fa:aa:47:35:5a:0f:e3:7b:15:08:ac:6b:f3:88:60:d3:c6:c2:e5:b1:2a:3c:aa:f2:a7:00:5a:72:41:eb:aa:77:71:11:2c:74:cf:9a:06:34:65:2f:bc:a0:e5:98:0c:54:a6:47:61:ea:10:1a:11:4e:0f:0b:55:72:ad:d5:7d:01:0b:7c:9c:88:7e:10:4c:a4:ee:12:72:da:66:d9:97:b9:a9:0b:5a:6d:62:4a:b6:c5:7e:73:c8:f9:19:00:0e:b5:f6:84:89:8e:f8:c3:db:ef:b3:30:c6:26:60:be:d8:8e:a7:8e:90:9a:ff:05:f6:da:62:7b sha1 ee:15:11:ce:c7:10:20:a9:b9:04:43:b3:7b:1d:5f:6e:70:30:30:f6 +a0:00:00:00:03 92 000000 rsa 03 99:6a:f5:6f:56:91:87:d0:92:93:c1:48:10:45:0e:d8:ee:33:57:39:7b:18:a2:45:8e:fa:a9:2d:a3:b6:df:65:14:ec:06:01:95:31:8f:d4:3b:e9:b8:f0:cc:66:9e:3f:84:40:57:cb:dd:f8:bd:a1:91:bb:64:47:3b:c8:dc:9a:73:0d:b8:f6:b4:ed:e3:92:41:86:ff:d9:b8:c7:73:57:89:c2:3a:36:ba:0b:8a:f6:53:72:eb:57:ea:5d:89:e7:d1:4e:9c:7b:6b:55:74:60:f1:08:85:da:16:ac:92:3f:15:af:37:58:f0:f0:3e:bd:3c:5c:2c:94:9c:ba:30:6d:b4:4e:6a:2c:07:6c:5f:67:e2:81:d7:ef:56:78:5d:c4:d7:59:45:e4:91:f0:19:18:80:0a:9e:2d:c6:6f:60:08:05:66:ce:0d:af:8d:17:ea:d4:6a:d8:e3:0a:24:7c:9f sha1 42:9c:95:4a:38:59:ce:f9:12:95:f6:63:c9:63:e5:82:ed:6e:b2:53 +a0:00:00:00:03 94 000000 rsa 03 ac:d2:b1:23:02:ee:64:4f:3f:83:5a:bd:1f:c7:a6:f6:2c:ce:48:ff:ec:62:2a:a8:ef:06:2b:ef:6f:b8:ba:8b:c6:8b:bf:6a:b5:87:0e:ed:57:9b:c3:97:3e:12:13:03:d3:48:41:a7:96:d6:dc:bc:41:db:f9:e5:2c:46:09:79:5c:0c:cf:7e:e8:6f:a1:d5:cb:04:10:71:ed:2c:51:d2:20:2f:63:f1:15:6c:58:a9:2d:38:bc:60:bd:f4:24:e1:77:6e:2b:c9:64:80:78:a0:3b:36:fb:55:43:75:fc:53:d5:7c:73:f5:16:0e:a5:9f:3a:fc:53:98:ec:7b:67:75:8d:65:c9:bf:f7:82:8b:6b:82:d4:be:12:4a:41:6a:b7:30:19:14:31:1e:a4:62:c1:9f:77:1f:31:b3:b5:73:36:00:0d:ff:73:2d:3b:83:de:07:05:2d:73:03:54:d2:97:be:c7:28:71:dc:cf:0e:19:3f:17:1a:ba:27:ee:46:4c:6a:97:69:09:43:d5:9b:da:bb:2a:27:eb:71:ce:eb:da:fa:11:76:04:64:78:fd:62:fe:c4:52:d5:ca:39:32:96:53:0a:a3:f4:19:27:ad:fe:43:4a:2d:f2:ae:30:54:f8:84:06:57:a2:6e:0f:c6:17 sha1 c4:a3:c4:3c:cf:87:32:7d:13:6b:80:41:60:e4:7d:43:b6:0e:6e:0f +a0:00:00:00:04 03 000000 rsa 03 c2:49:07:47:fe:17:eb:05:84:c8:8d:47:b1:60:27:04:15:0a:dc:88:c5:b9:98:bd:59:ce:04:3e:de:bf:0f:fe:e3:09:3a:c7:95:6a:d3:b6:ad:45:54:c6:de:19:a1:78:d6:da:29:5b:e1:5d:52:20:64:5e:3c:81:31:66:6f:a4:be:5b:84:fe:13:1e:a4:4b:03:93:07:63:8b:9e:74:a8:c4:25:64:f8:92:a6:4d:f1:cb:15:71:2b:73:6e:33:74:f1:bb:b6:81:93:71:60:2d:89:70:e9:7b:90:07:93:c7:c2:a8:9a:4a:16:49:a5:9b:e6:80:57:4d:d0:b6:01:45 sha1 5a:dd:f2:1d:09:27:86:61:14:11:79:cb:ef:f2:72:ea:38:4b:13:bb +a0:00:00:00:04 04 000000 rsa 03 a6:da:42:83:87:a5:02:d7:dd:fb:7a:74:d3:f4:12:be:76:26:27:19:7b:25:43:5b:7a:81:71:6a:70:01:57:dd:d0:6f:7c:c9:9d:6c:a2:8c:24:70:52:7e:2c:03:61:6b:9c:59:21:73:57:c2:67:4f:58:3b:3b:a5:c7:dc:f2:83:86:92:d0:23:e3:56:24:20:b4:61:5c:43:9c:a9:7c:44:dc:9a:24:9c:fc:e7:b3:bf:b2:2f:68:22:8c:3a:f1:33:29:aa:4a:61:3c:f8:dd:85:35:02:37:3d:62:e4:9a:b2:56:d2:bc:17:12:0e:54:ae:dc:ed:6d:96:a4:28:7a:cc:5c:04:67:7d:4a:5a:32:0d:b8:be:e2:f7:75:e5:fe:c5 sha1 38:1a:03:5d:a5:8b:48:2e:e2:af:75:f4:c3:f2:ca:46:9b:a4:aa:6c +a0:00:00:00:04 05 000000 rsa 03 b8:04:8a:bc:30:c9:0d:97:63:36:54:3e:3f:d7:09:1c:8f:e4:80:0d:f8:20:ed:55:e7:e9:48:13:ed:00:55:5b:57:3f:ec:a3:d8:4a:f6:13:1a:65:1d:66:cf:f4:28:4f:b1:3b:63:5e:dd:0e:e4:01:76:d8:bf:04:b7:fd:1c:7b:ac:f9:ac:73:27:df:aa:8a:a7:2d:10:db:3b:8e:70:b2:dd:d8:11:cb:41:96:52:5e:a3:86:ac:c3:3c:0d:9d:45:75:91:64:69:c4:e4:f5:3e:8e:1c:91:2c:c6:18:cb:22:dd:e7:c3:56:8e:90:02:2e:6b:ba:77:02:02:e4:52:2a:2d:d6:23:d1:80:e2:15:bd:1d:15:07:fe:3d:c9:0c:a3:10:d2:7b:3e:fc:cd:8f:83:de:30:52:ca:d1:e4:89:38:c6:8d:09:5a:ac:91:b5:f3:7e:28:bb:49:ec:7e:d5:97 sha1 eb:fa:0d:5d:06:d8:ce:70:2d:a3:ea:e8:90:70:1d:45:e2:74:c8:45 +a0:00:00:00:04 06 000000 rsa 03 cb:26:fc:83:0b:43:78:5b:2b:ce:37:c8:1e:d3:34:62:2f:96:22:f4:c8:9a:ae:64:10:46:b2:35:34:33:88:3f:30:7f:b7:c9:74:16:2d:a7:2f:7a:4e:c7:5d:9d:65:73:36:86:5b:8d:30:23:d3:d6:45:66:76:25:c9:a0:7a:6b:7a:13:7c:f0:c6:41:98:ae:38:fc:23:80:06:fb:26:03:f4:1f:4f:3b:b9:da:13:47:27:0f:2f:5d:8c:60:6e:42:09:58:c5:f7:d5:0a:71:de:30:14:2f:70:de:46:88:89:b5:e3:a0:86:95:b9:38:a5:0f:c9:80:39:3a:9c:bc:e4:4a:d2:d6:4f:63:0b:b3:3a:d3:f5:f5:fd:49:5d:31:f3:78:18:c1:d9:40:71:34:2e:07:f1:be:c2:19:4f:60:35:ba:5d:ed:39:36:50:0e:b8:2d:fd:a6:e8:af:b6:55:b1:ef:3d:0d:7e:bf:86:b6:6d:d9:f2:9f:6b:1d:32:4f:e8:b2:6c:e3:8a:b2:01:3d:d1:3f:61:1e:7a:59:4d:67:5c:44:32:35:0e:a2:44:cc:34:f3:87:3c:ba:06:59:29:87:a1:d7:e8:52:ad:c2:2e:f5:a2:ee:28:13:20:31:e4:8f:74:03:7e:3b:34:ab:74:7f sha1 f9:10:a1:50:4d:5f:fb:79:3d:94:f3:b5:00:76:5e:1a:bc:ad:72:d9 +a0:00:00:00:04 00 000000 rsa 03 9c:6b:e5:ad:b1:0b:4b:e3:dc:e2:09:9b:4b:21:06:72:b8:96:56:eb:a0:91:20:4f:61:3e:cc:62:3b:ed:c9:c6:d7:7b:66:0e:8b:ae:ea:7f:7c:e3:0f:1b:15:38:79:a4:e3:64:59:34:3d:1f:e4:7a:cd:bd:41:fc:d7:10:03:0c:2b:a1:d9:46:15:97:98:2c:6e:1b:dd:08:55:4b:72:6f:5e:ff:79:13:ce:59:e7:9e:35:72:95:c3:21:e2:6d:0b:8b:e2:70:a9:44:23:45:c7:53:e2:aa:2a:cf:c9:d3:08:50:60:2f:e6:ca:c0:0c:6d:df:6b:8d:9d:9b:48:79:b2:82:6b:04:2a:07:f0:e5:ae:52:6a:3d:3c:4d:22:c7:2b:9e:aa:52:ee:d8:89:38:66:f8:66:38:7a:c0:5a:13:99 sha1 ec:0a:59:d3:5d:19:f0:31:e9:e8:cb:ec:56:db:80:e2:2b:1d:e1:30 +a0:00:00:00:04 02 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 33:40:8b:96:c8:14:74:2a:d7:35:36:c7:2f:09:26:e4:47:1e:8e:47 +a0:00:00:00:04 05 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 53:d0:49:03:b4:96:f5:95:44:a8:43:09:af:16:92:51:f2:89:68:74 +a0:00:00:00:04 ef 000000 rsa 03 a1:91:cb:87:47:3f:29:34:9b:5d:60:a8:8b:3e:ae:e0:97:3a:a6:f1:a0:82:f3:58:d8:49:fd:df:f9:c0:91:f8:99:ed:a9:79:2c:af:09:ef:28:f5:d2:24:04:b8:8a:22:93:ee:bb:c1:94:9c:43:be:a4:d6:0c:fd:87:9a:15:39:54:4e:09:e0:f0:9f:60:f0:65:b2:bf:2a:13:ec:c7:05:f3:d4:68:b9:d3:3a:e7:7a:d9:d3:f1:9c:a4:0f:23:dc:f5:eb:7c:04:dc:8f:69:eb:a5:65:b1:eb:cb:46:86:cd:27:47:85:53:0f:f6:f6:e9:ee:43:aa:43:fd:b0:2c:e0:0d:ae:c1:5c:7b:8f:d6:a9:b3:94:ba:ba:41:9d:3f:6d:c8:5e:16:56:9b:e8:e7:69:89:68:8e:fe:a2:df:22:ff:7d:35:c0:43:33:8d:ea:a9:82:a0:2b:86:6d:e5:32:85:19:eb:bc:d6:f0:3c:dd:68:66:73:84:7f:84:db:65:1a:b8:6c:28:cf:14:62:56:2c:57:7b:85:35:64:a2:90:c8:55:6d:81:85:31:26:8d:25:cc:98:a4:cc:6a:0b:df:ff:da:2d:cc:a3:a9:4c:99:85:59:e3:07:fd:df:91:50:06:d9:a9:87:b0:7d:da:eb:3b sha1 21:76:6e:bb:0e:e1:22:af:b6:5d:78:45:b7:3d:b4:6b:ab:65:42:7a +a0:00:00:00:04 f1 000000 rsa 03 a0:dc:f4:bd:e1:9c:35:46:b4:b6:f0:41:4d:17:4d:de:29:4a:ab:bb:82:8c:5a:83:4d:73:aa:e2:7c:99:b0:b0:53:a9:02:78:00:72:39:b6:45:9f:f0:bb:cd:7b:4b:9c:6c:50:ac:02:ce:91:36:8d:a1:bd:21:aa:ea:db:c6:53:47:33:7d:89:b6:8f:5c:99:a0:9d:05:be:02:dd:1f:8c:5b:a2:0e:2f:13:fb:2a:27:c4:1d:3f:85:ca:d5:cf:66:68:e7:58:51:ec:66:ed:bf:98:85:1f:d4:e4:2c:44:c1:d5:9f:59:84:70:3b:27:d5:b9:f2:1b:8f:a0:d9:32:79:fb:bf:69:e0:90:64:29:09:c9:ea:27:f8:98:95:95:41:aa:67:57:f5:f6:24:10:4f:6e:1d:3a:95:32:f2:a6:e5:15:15:ae:ad:1b:43:b3:d7:83:50:88:a2:fa:fa:7b:e7 sha1 d8:e6:8d:a1:67:ab:5a:85:d8:c3:d5:5e:cb:9b:05:17:a1:a5:b4:bb +a0:00:00:00:04 f3 000000 rsa 03 98:f0:c7:70:f2:38:64:c2:e7:66:df:02:d1:e8:33:df:f4:ff:e9:2d:69:6e:16:42:f0:a8:8c:56:94:c6:47:9d:16:db:15:37:bf:e2:9e:4f:dc:6e:6e:8a:fd:1b:0e:b7:ea:01:24:72:3c:33:31:79:bf:19:e9:3f:10:65:8b:2f:77:6e:82:9e:87:da:ed:a9:c9:4a:8b:33:82:19:9a:35:0c:07:79:77:c9:7a:ff:08:fd:11:31:0a:c9:50:a7:2c:3c:a5:00:2e:f5:13:fc:cc:28:6e:64:6e:3c:53:87:53:5d:50:95:14:b3:b3:26:e1:23:4f:9c:b4:8c:36:dd:d4:4b:41:6d:23:65:40:34:a6:6f:40:3b:a5:11:c5:ef:a3 sha1 a6:9a:c7:60:3d:af:56:6e:97:2d:ed:c2:cb:43:3e:07:e8:b0:1a:9a +a0:00:00:00:04 f5 000000 rsa 01:00:01 a6:e6:fb:72:17:95:06:f8:60:cc:ca:8c:27:f9:9c:ec:d9:4c:7d:4f:31:91:d3:03:bb:ee:37:48:1c:7a:a1:5f:23:3b:a7:55:e9:e4:37:63:45:a9:a6:7e:79:94:bd:c1:c6:80:bb:35:22:d8:c9:3e:b0:cc:c9:1a:d3:1a:d4:50:da:30:d3:37:66:2d:19:ac:03:e2:b4:ef:5f:6e:c1:82:82:d4:91:e1:97:67:d7:b2:45:42:df:de:ff:6f:62:18:55:03:53:20:69:bb:b3:69:e3:bb:9f:b1:9a:c6:f1:c3:0b:97:d2:49:ee:e7:64:e0:ba:c9:7f:25:c8:73:d9:73:95:3e:51:53:a4:20:64:bb:fa:bf:d0:6a:4b:b4:86:86:0b:f6:63:74:06:c9:fc:36:81:3a:4a:75:f7:5c:31:cc:a9:f6:9f:8d:e5:9a:de:ce:f6:bd:e7:e0:78:00:fc:be:03:5d:31:76:af:84:73:e2:3e:9a:a3:df:ee:22:11:96:d1:14:83:02:67:7c:72:0c:fe:25:44:a0:3d:b5:53:e7:f1:b8:42:7b:a1:cc:72:b0:f2:9b:12:df:ef:4c:08:1d:07:6d:35:3e:71:88:0a:ad:ff:38:63:52:af:0a:b7:b2:8e:d4:9e:1e:67:2d:11:f9 sha1 c2:23:98:04:c8:09:81:70:be:52:d6:d5:d4:15:9e:81:ce:84:66:bf +a0:00:00:00:04 f6 000000 rsa 03 a2:5a:6b:d7:83:a5:ef:6b:8f:b6:f8:30:55:c2:60:f5:f9:9e:a1:66:78:f3:b9:05:3e:0f:64:98:e8:2c:3f:5d:1e:8c:38:f1:35:88:01:7e:2b:12:b3:d8:ff:6f:50:16:7f:46:44:29:10:72:9e:9e:4d:1b:37:39:e5:06:7c:0a:c7:a1:f4:48:7e:35:f6:75:bc:16:e2:33:31:51:65:cb:14:2b:fd:b2:5e:30:1a:63:2a:54:a3:37:1e:ba:b6:57:2d:ee:ba:f3:70:f3:37:f0:57:ee:73:b4:ae:46:d1:a8:bc:4d:a8:53:ec:3c:c1:2c:8c:bc:2d:a1:83:22:d6:85:30:c7:0b:22:bd:ac:35:1d:d3:60:68:ae:32:1e:11:ab:f2:64:f4:d3:56:9b:b7:12:14:54:50:05:55:8d:e2:60:83:c7:35:db:77:63:68:17:2f:e8:c2:f5:c8:5e:8b:5b:89:0c:c6:82:91:1d:2d:e7:1f:a6:26:b8:81:7f:cc:c0:89:22:b7:03:86:9f:3b:ae:ac:14:59:d7:7c:d8:53:76:bc:36:18:2f:42:38:31:4d:6c:42:12:fb:dd:7f:23:d3 sha1 50:29:09:ed:54:5e:3c:8d:bd:00:ea:58:2d:06:17:fe:e9:f6:f6:84 +a0:00:00:00:04 f7 000000 rsa 01:00:01 94:ea:62:f6:d5:83:20:e3:54:c0:22:ad:dc:f0:55:9d:8c:f2:06:cd:92:e8:69:56:49:05:ce:21:d7:20:f9:71:b7:ae:a3:74:83:0e:be:17:57:11:5a:85:e0:88:d4:1c:6b:77:cf:5e:c8:21:f3:0b:1d:89:04:17:bf:2f:a3:1e:59:08:de:d5:fa:67:7f:8c:7b:18:4a:d0:90:28:fd:de:96:b6:a6:10:98:50:aa:80:01:75:ea:bc:db:bb:68:4a:96:c2:eb:63:79:df:ea:08:d3:2f:e2:33:1f:e1:03:23:3a:d5:8d:cd:b1:e6:e0:77:cb:9f:24:ea:ec:5c:25:af sha1 ee:b0:dd:9b:24:77:be:e3:20:9a:91:4c:db:a9:4c:1c:4a:9b:de:d9 +a0:00:00:00:04 f8 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 f0:6e:cc:6d:2a:ae:bf:25:9b:7e:75:5a:38:d9:a9:b2:4e:2f:f3:dd +a0:00:00:00:04 f9 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 33:67:12:dc:c2:85:54:80:9c:6a:a9:b0:23:58:de:6f:75:51:64:db +a0:00:00:00:04 fa 000000 rsa 03 a9:0f:cd:55:aa:2d:5d:99:63:e3:5e:d0:f4:40:17:76:99:83:2f:49:c6:ba:b1:5c:da:e5:79:4b:e9:3f:93:4d:44:62:d5:d1:27:62:e4:8c:38:ba:83:d8:44:5d:ea:a7:41:95:a3:01:a1:02:b2:f1:14:ea:da:0d:18:0e:e5:e7:a5:c7:3e:0c:4e:11:f6:7a:43:dd:ab:5d:55:68:3b:14:74:cc:06:27:f4:4b:8d:30:88:a4:92:ff:aa:da:d4:f4:24:22:d0:e7:01:35:36:c3:c4:9a:d3:d0:fa:e9:64:59:b0:f6:b1:b6:05:65:38:a3:d6:d4:46:40:f9:44:67:b1:08:86:7d:ec:40:fa:ae:cd:74:0c:00:e2:b7:a8:85:2d sha1 5b:ed:40:68:d9:6e:a1:6d:2d:77:e0:3d:60:36:fc:7a:16:0e:a9:9c +b0:12:34:56:78 00 000000 rsa 03 9c:6b:e5:ad:b1:0b:4b:e3:dc:e2:09:9b:4b:21:06:72:b8:96:56:eb:a0:91:20:4f:61:3e:cc:62:3b:ed:c9:c6:d7:7b:66:0e:8b:ae:ea:7f:7c:e3:0f:1b:15:38:79:a4:e3:64:59:34:3d:1f:e4:7a:cd:bd:41:fc:d7:10:03:0c:2b:a1:d9:46:15:97:98:2c:6e:1b:dd:08:55:4b:72:6f:5e:ff:79:13:ce:59:e7:9e:35:72:95:c3:21:e2:6d:0b:8b:e2:70:a9:44:23:45:c7:53:e2:aa:2a:cf:c9:d3:08:50:60:2f:e6:ca:c0:0c:6d:df:6b:8d:9d:9b:48:79:b2:82:6b:04:2a:07:f0:e5:ae:52:6a:3d:3c:4d:22:c7:2b:9e:aa:52:ee:d8:89:38:66:f8:66:38:7a:c0:5a:13:99 sha1 5d:29:70:e6:46:75:72:7e:60:46:07:65:a8:db:75:34:2a:e1:47:83 +b0:12:34:56:78 02 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 29:4b:e2:02:39:ab:15:24:5a:63:be:a4:6c:c6:c1:75:a2:55:62:d1 +b0:12:34:56:78 05 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 b9:a1:d6:5c:af:e0:6b:05:4e:dd:7e:a8:25:97:ab:85:f1:30:e6:63 +b0:12:34:56:78 f3 000000 rsa 01:00:01 94:ea:62:f6:d5:83:20:e3:54:c0:22:ad:dc:f0:55:9d:8c:f2:06:cd:92:e8:69:56:49:05:ce:21:d7:20:f9:71:b7:ae:a3:74:83:0e:be:17:57:11:5a:85:e0:88:d4:1c:6b:77:cf:5e:c8:21:f3:0b:1d:89:04:17:bf:2f:a3:1e:59:08:de:d5:fa:67:7f:8c:7b:18:4a:d0:90:28:fd:de:96:b6:a6:10:98:50:aa:80:01:75:ea:bc:db:bb:68:4a:96:c2:eb:63:79:df:ea:08:d3:2f:e2:33:1f:e1:03:23:3a:d5:8d:cd:b1:e6:e0:77:cb:9f:24:ea:ec:5c:25:af sha1 56:94:b0:d2:78:48:18:14:a0:5e:12:b5:58:ce:c1:23:48:65:aa:5d +b0:12:34:56:78 f5 000000 rsa 03 a2:5a:6b:d7:83:a5:ef:6b:8f:b6:f8:30:55:c2:60:f5:f9:9e:a1:66:78:f3:b9:05:3e:0f:64:98:e8:2c:3f:5d:1e:8c:38:f1:35:88:01:7e:2b:12:b3:d8:ff:6f:50:16:7f:46:44:29:10:72:9e:9e:4d:1b:37:39:e5:06:7c:0a:c7:a1:f4:48:7e:35:f6:75:bc:16:e2:33:31:51:65:cb:14:2b:fd:b2:5e:30:1a:63:2a:54:a3:37:1e:ba:b6:57:2d:ee:ba:f3:70:f3:37:f0:57:ee:73:b4:ae:46:d1:a8:bc:4d:a8:53:ec:3c:c1:2c:8c:bc:2d:a1:83:22:d6:85:30:c7:0b:22:bd:ac:35:1d:d3:60:68:ae:32:1e:11:ab:f2:64:f4:d3:56:9b:b7:12:14:54:50:05:55:8d:e2:60:83:c7:35:db:77:63:68:17:2f:e8:c2:f5:c8:5e:8b:5b:89:0c:c6:82:91:1d:2d:e7:1f:a6:26:b8:81:7f:cc:c0:89:22:b7:03:86:9f:3b:ae:ac:14:59:d7:7c:d8:53:76:bc:36:18:2f:42:38:31:4d:6c:42:12:fb:dd:7f:23:d3 sha1 f7:5e:88:02:85:5c:9b:14:02:7e:51:73:45:71:7e:5c:36:35:b9:1b +b0:12:34:56:78 f6 000000 rsa 03 a1:f5:e1:c9:bd:86:50:bd:43:ab:6e:e5:6b:89:1e:f7:45:9c:0a:24:fa:84:f9:12:7d:1a:6c:79:d4:93:0f:6d:b1:85:2e:25:10:f1:8b:61:cd:35:4d:b8:3a:35:6b:d1:90:b8:8a:b8:df:04:28:4d:02:a4:20:4a:7b:6c:b7:c5:55:19:77:a9:b3:63:79:ca:3d:e1:a0:8e:69:f3:01:c9:5c:c1:c2:05:06:95:92:75:f4:17:23:dd:5d:29:25:29:05:79:e5:a9:5b:0d:f6:32:3f:c8:e9:27:3d:6f:84:91:98:c4:99:62:09:16:6d:9b:fc:97:3c:36:1c:c8:26:e1 sha1 e9:40:6b:65:10:c1:43:ab:1e:9b:9d:79:a3:c1:df:f8:90:9a:34:7c +b0:12:34:56:78 f7 000000 rsa 03 98:f0:c7:70:f2:38:64:c2:e7:66:df:02:d1:e8:33:df:f4:ff:e9:2d:69:6e:16:42:f0:a8:8c:56:94:c6:47:9d:16:db:15:37:bf:e2:9e:4f:dc:6e:6e:8a:fd:1b:0e:b7:ea:01:24:72:3c:33:31:79:bf:19:e9:3f:10:65:8b:2f:77:6e:82:9e:87:da:ed:a9:c9:4a:8b:33:82:19:9a:35:0c:07:79:77:c9:7a:ff:08:fd:11:31:0a:c9:50:a7:2c:3c:a5:00:2e:f5:13:fc:cc:28:6e:64:6e:3c:53:87:53:5d:50:95:14:b3:b3:26:e1:23:4f:9c:b4:8c:36:dd:d4:4b:41:6d:23:65:40:34:a6:6f:40:3b:a5:11:c5:ef:a3 sha1 f7:81:13:e8:60:f0:30:a8:72:92:3f:ce:93:e3:38:1c:77:a4:2a:30 +b0:12:34:56:78 f8 000000 rsa 03 a9:9a:6d:3e:07:18:89:ed:9e:3a:0c:39:1c:69:b0:b8:04:fc:16:0b:2b:4b:dd:57:0c:92:dd:5a:0f:45:f5:3e:86:21:f7:c9:6c:40:22:42:66:73:5e:1e:e1:b3:c0:62:38:ae:35:04:63:20:fd:8e:81:f8:ce:b3:f8:b4:c9:7b:94:09:30:a3:ac:5e:79:00:86:da:d4:1a:6a:4f:51:17:ba:1c:e2:43:8a:51:ac:05:3e:b0:02:ae:d8:66:d2:c4:58:fd:73:35:90:21:a1:20:29:a0:c0:43:04:5c:11:66:4f:e0:21:9e:c6:3c:10:bf:21:55:bb:27:84:60:9a:10:64:21:d4:51:63:79:97:38:c1:c3:09:09:bb:6c:6f:e5:2b:bb:76:39:7b:97:40:ce:06:4a:61:3f:f8:41:11:85:f0:88:42:a4:23:ea:d2:0e:df:fb:ff:1c:d6:c3:fe:0c:98:21:47:91:99:c2:6d:85:72:cc:8a:ff:f0:87:a9:c3 sha1 66:46:9c:88:e7:dc:11:15:29:c7:d3:79:d7:93:8c:8d:f3:e4:c2:5e +b0:12:34:56:78 f9 000000 rsa 01:00:01 a6:e6:fb:72:17:95:06:f8:60:cc:ca:8c:27:f9:9c:ec:d9:4c:7d:4f:31:91:d3:03:bb:ee:37:48:1c:7a:a1:5f:23:3b:a7:55:e9:e4:37:63:45:a9:a6:7e:79:94:bd:c1:c6:80:bb:35:22:d8:c9:3e:b0:cc:c9:1a:d3:1a:d4:50:da:30:d3:37:66:2d:19:ac:03:e2:b4:ef:5f:6e:c1:82:82:d4:91:e1:97:67:d7:b2:45:42:df:de:ff:6f:62:18:55:03:53:20:69:bb:b3:69:e3:bb:9f:b1:9a:c6:f1:c3:0b:97:d2:49:ee:e7:64:e0:ba:c9:7f:25:c8:73:d9:73:95:3e:51:53:a4:20:64:bb:fa:bf:d0:6a:4b:b4:86:86:0b:f6:63:74:06:c9:fc:36:81:3a:4a:75:f7:5c:31:cc:a9:f6:9f:8d:e5:9a:de:ce:f6:bd:e7:e0:78:00:fc:be:03:5d:31:76:af:84:73:e2:3e:9a:a3:df:ee:22:11:96:d1:14:83:02:67:7c:72:0c:fe:25:44:a0:3d:b5:53:e7:f1:b8:42:7b:a1:cc:72:b0:f2:9b:12:df:ef:4c:08:1d:07:6d:35:3e:71:88:0a:ad:ff:38:63:52:af:0a:b7:b2:8e:d4:9e:1e:67:2d:11:f9 sha1 ae:ac:a4:54:80:c8:83:4c:b0:be:bd:cc:57:0b:7b:2b:74:bb:4b:79 diff --git a/client/emv/cmdemv.c b/client/emv/cmdemv.c new file mode 100644 index 00000000..d886834d --- /dev/null +++ b/client/emv/cmdemv.c @@ -0,0 +1,1429 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017, 2018 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// EMV commands +//----------------------------------------------------------------------------- + +#include +#include "mifare.h" +#include "cmdemv.h" +#include "emvjson.h" +#include "emv_pki.h" +#include "test/cryptotest.h" +#include "cliparser/cliparser.h" +#include + +#define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) +void ParamLoadDefaults(struct tlvdb *tlvRoot) { + //9F02:(Amount, authorized (Numeric)) len:6 + TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); + //9F1A:(Terminal Country Code) len:2 + TLV_ADD(0x9F1A, "ru"); + //5F2A:(Transaction Currency Code) len:2 + // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 + TLV_ADD(0x5F2A, "\x09\x80"); + //9A:(Transaction Date) len:3 + TLV_ADD(0x9A, "\x00\x00\x00"); + //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + TLV_ADD(0x9C, "\x00"); + // 9F37 Unpredictable Number len:4 + TLV_ADD(0x9F37, "\x01\x02\x03\x04"); + // 9F6A Unpredictable Number (MSD for UDOL) len:4 + TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC +} + +int CmdHFEMVSelect(const char *cmd) { + uint8_t data[APDU_AID_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv select", + "Executes select applet command", + "Usage:\n\thf emv select -s a00000000101 -> select card, select applet\n\thf emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results"), + arg_strx0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + bool APDULogging = arg_get_lit(3); + bool decodeTLV = arg_get_lit(4); + CLIGetHexWithReturn(5, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVSelect(activateField, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVSearch(const char *cmd) { + + CLIParserInit("hf emv search", + "Tries to select all applets from applet list:\n", + "Usage:\n\thf emv search -s -> select card and search\n\thf emv search -st -> select card, search and show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + bool APDULogging = arg_get_lit(3); + bool decodeTLV = arg_get_lit(4); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + struct tlvdb *t = NULL; + const char *al = "Applets list"; + t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + if (EMVSearch(activateField, leaveSignalON, decodeTLV, t)) { + tlvdb_free(t); + return 2; + } + + PrintAndLog("Search completed."); + + // print list here + if (!decodeTLV) { + TLVPrintAIDlistFromSelectTLV(t); + } + + tlvdb_free(t); + + return 0; +} + +int CmdHFEMVPPSE(const char *cmd) { + + CLIParserInit("hf emv pse", + "Executes PSE/PPSE select command. It returns list of applet on the card:\n", + "Usage:\n\thf emv pse -s1 -> select, get pse\n\thf emv pse -st2 -> select, get ppse, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card"), + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("1", "pse", "pse (1PAY.SYS.DDF01) mode"), + arg_lit0("2", "ppse", "ppse (2PAY.SYS.DDF01) mode (default mode)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool activateField = arg_get_lit(1); + bool leaveSignalON = arg_get_lit(2); + uint8_t PSENum = 2; + if (arg_get_lit(3)) + PSENum = 1; + if (arg_get_lit(4)) + PSENum = 2; + bool APDULogging = arg_get_lit(5); + bool decodeTLV = arg_get_lit(6); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVSelectPSE(activateField, leaveSignalON, PSENum, buf, sizeof(buf), &len, &sw); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVGPO(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv gpo", + "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", + "Usage:\n\thf emv gpo -k -> execute GPO\n" + "\thf emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n" + "\thf emv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for PDOLdata making from PDOL and parameters"), + arg_lit0("mM", "make", "make PDOLdata from PDOL (tag 9F38) and parameters (by default uses default parameters)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_strx0(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool paramsLoadFromFile = arg_get_lit(2); + bool dataMakeFromPDOL = arg_get_lit(3); + bool APDULogging = arg_get_lit(4); + bool decodeTLV = arg_get_lit(5); + CLIGetHexWithReturn(6, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc PDOL + struct tlv *pdol_data_tlv = NULL; + struct tlv data_tlv = { + .tag = 0x83, + .len = datalen, + .value = (uint8_t *)data, + }; + if (dataMakeFromPDOL) { + ParamLoadDefaults(tlvRoot); + + if (paramsLoadFromFile) { + PrintAndLog("Params loading from file..."); + ParamLoadFromJson(tlvRoot); + }; + + pdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f38, datalen, data), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("ERROR: can't create PDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + } + } else { + if (paramsLoadFromFile) { + PrintAndLog("WARNING: don't need to load parameters. Sending plain PDOL data..."); + } + pdol_data_tlv = &data_tlv; + } + + size_t pdol_data_tlv_data_len = 0; + unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); + if (!pdol_data_tlv_data) { + PrintAndLog("ERROR: can't create PDOL data."); + tlvdb_free(tlvRoot); + return 4; + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVGPO(leaveSignalON, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (pdol_data_tlv != &data_tlv) + free(pdol_data_tlv); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVReadRecord(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv readrec", + "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.", + "Usage:\n\thf emv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\thf emv readrec -kt 0201-> read file 0201 and show result in TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + CLIGetHexWithReturn(4, data, &datalen); + CLIParserFree(); + + if (datalen != 2) { + PrintAndLog("ERROR: Command needs to have 2 bytes of data"); + return 1; + } + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVReadRecord(leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVAC(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv genac", + "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" + "\thf emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" + "\thf emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n" + "\thf emv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("cC", "cda", "executes CDA transaction. Needs to get SDAD in results."), + arg_str0("dD", "decision", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), + arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for CDOLdata making from CDOL and parameters"), + arg_lit0("mM", "make", "make CDOLdata from CDOL (tag 8C and 8D) and parameters (by default uses default parameters)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool leaveSignalON = arg_get_lit(1); + bool trTypeCDA = arg_get_lit(2); + uint8_t termDecision = 0xff; + if (arg_get_str_len(3)) { + if (!strncmp(arg_get_str(3)->sval[0], "aac", 4)) + termDecision = EMVAC_AAC; + if (!strncmp(arg_get_str(3)->sval[0], "tc", 4)) + termDecision = EMVAC_TC; + if (!strncmp(arg_get_str(3)->sval[0], "arqc", 4)) + termDecision = EMVAC_ARQC; + + if (termDecision == 0xff) { + PrintAndLog("ERROR: can't find terminal decision '%s'", arg_get_str(3)->sval[0]); + return 1; + } + } else { + termDecision = EMVAC_TC; + } + if (trTypeCDA) + termDecision = termDecision | EMVAC_CDAREQ; + bool paramsLoadFromFile = arg_get_lit(4); + bool dataMakeFromCDOL = arg_get_lit(5); + bool APDULogging = arg_get_lit(6); + bool decodeTLV = arg_get_lit(7); + CLIGetHexWithReturn(8, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc CDOL + struct tlv *cdol_data_tlv = NULL; + struct tlv data_tlv = { + .tag = 0x01, + .len = datalen, + .value = (uint8_t *)data, + }; + + if (dataMakeFromCDOL) { + ParamLoadDefaults(tlvRoot); + + if (paramsLoadFromFile) { + PrintAndLog("Params loading from file..."); + ParamLoadFromJson(tlvRoot); + }; + + cdol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x8c, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag + if (!cdol_data_tlv){ + PrintAndLog("ERROR: can't create CDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + } + } else { + if (paramsLoadFromFile) { + PrintAndLog("WARNING: don't need to load parameters. Sending plain CDOL data..."); + } + cdol_data_tlv = &data_tlv; + } + + PrintAndLog("CDOL data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVAC(leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (cdol_data_tlv != &data_tlv) + free(cdol_data_tlv); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +int CmdHFEMVGenerateChallenge(const char *cmd) { + + CLIParserInit("hf emv challenge", + "Executes Generate Challenge command. It returns 4 or 8-byte random number from card.\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv challenge -> get challenge\n\thf emv challenge -k -> get challenge, keep fileld ON\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool leaveSignalON = arg_get_lit(1); + bool APDULogging = arg_get_lit(2); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVGenerateChallenge(leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + PrintAndLog("Challenge: %s", sprint_hex(buf, len)); + + if (len != 4 && len != 8) + PrintAndLog("WARNING: length of challenge must be 4 or 8, but it %d", len); + + return 0; +} + +int CmdHFEMVInternalAuthenticate(const char *cmd) { + uint8_t data[APDU_RES_LEN] = {0}; + int datalen = 0; + + CLIParserInit("hf emv intauth", + "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", + "Usage:\n\thf emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" + "\thf emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n" + "\thf emv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("kK", "keep", "keep field ON for next command"), + arg_lit0("pP", "params", "load parameters from `emv/defparams.json` file for DDOLdata making from DDOL and parameters"), + arg_lit0("mM", "make", "make DDOLdata from DDOL (tag 9F49) and parameters (by default uses default parameters)"), + arg_lit0("aA", "apdu", "show APDU reqests and responses"), + arg_lit0("tT", "tlv", "TLV decode results of selected applets"), + arg_strx1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, false); + + bool leaveSignalON = arg_get_lit(1); + bool paramsLoadFromFile = arg_get_lit(2); + bool dataMakeFromDDOL = arg_get_lit(3); + bool APDULogging = arg_get_lit(4); + bool decodeTLV = arg_get_lit(5); + CLIGetHexWithReturn(6, data, &datalen); + CLIParserFree(); + + SetAPDULogging(APDULogging); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // calc DDOL + struct tlv *ddol_data_tlv = NULL; + struct tlv data_tlv = { + .tag = 0x01, + .len = datalen, + .value = (uint8_t *)data, + }; + + if (dataMakeFromDDOL) { + ParamLoadDefaults(tlvRoot); + + if (paramsLoadFromFile) { + PrintAndLog("Params loading from file..."); + ParamLoadFromJson(tlvRoot); + }; + + ddol_data_tlv = dol_process((const struct tlv *)tlvdb_external(0x9f49, datalen, data), tlvRoot, 0x01); // 0x01 - dummy tag + if (!ddol_data_tlv){ + PrintAndLog("ERROR: can't create DDOL TLV."); + tlvdb_free(tlvRoot); + return 4; + } + } else { + if (paramsLoadFromFile) { + PrintAndLog("WARNING: don't need to load parameters. Sending plain DDOL data..."); + } + ddol_data_tlv = &data_tlv; + } + + PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); + + // exec + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res = EMVInternalAuthenticate(leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + + if (ddol_data_tlv != &data_tlv) + free(ddol_data_tlv); + tlvdb_free(tlvRoot); + + if (sw) + PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + + if (res) + return res; + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + return 0; +} + +#define dreturn(n) {free(pdol_data_tlv);tlvdb_free(tlvSelect);tlvdb_free(tlvRoot);DropField();return n;} + +void InitTransactionParameters(struct tlvdb *tlvRoot, bool paramLoadJSON, enum TransactionType TrType, bool GenACGPO) { + + ParamLoadDefaults(tlvRoot); + + if (paramLoadJSON) { + PrintAndLog("* * Transaction parameters loading from JSON..."); + ParamLoadFromJson(tlvRoot); + } + + //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + char *qVSDC = "\x26\x00\x00\x00"; + if (GenACGPO) { + qVSDC = "\x26\x80\x00\x00"; + } + switch(TrType) { + case TT_MSD: + TLV_ADD(0x9F66, "\x86\x00\x00\x00"); // MSD + break; + // not standard for contactless. just for test. + case TT_VSDC: + TLV_ADD(0x9F66, "\x46\x00\x00\x00"); // VSDC + break; + case TT_QVSDCMCHIP: + TLV_ADD(0x9F66, qVSDC); // qVSDC + break; + case TT_CDA: + TLV_ADD(0x9F66, qVSDC); // qVSDC (VISA CDA not enabled) + break; + default: + break; + } +} + +void ProcessGPOResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t len, bool decodeTLV) { + if (buf[0] == 0x80) { + if (decodeTLV){ + PrintAndLog("GPO response format1:"); + TLVPrintFromBuffer(buf, len); + } + + if (len < 4 || (len - 4) % 4) { + PrintAndLog("ERROR: GPO response format1 parsing error. length=%d", len); + } else { + // AIP + struct tlvdb * f1AIP = tlvdb_fixed(0x82, 2, buf + 2); + tlvdb_add(tlvRoot, f1AIP); + if (decodeTLV){ + PrintAndLog("\n* * Decode response format 1 (0x80) AIP and AFL:"); + TLVPrintFromTLV(f1AIP); + } + + // AFL + struct tlvdb * f1AFL = tlvdb_fixed(0x94, len - 4, buf + 2 + 2); + tlvdb_add(tlvRoot, f1AFL); + if (decodeTLV) + TLVPrintFromTLV(f1AFL); + } + } else { + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + } +} + +int CmdHFEMVExec(const char *cmd) { + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t ODAiList[4096]; + size_t ODAiListLen = 0; + + int res; + + struct tlvdb *tlvSelect = NULL; + struct tlvdb *tlvRoot = NULL; + struct tlv *pdol_data_tlv = NULL; + + CLIParserInit("hf emv exec", + "Executes EMV contactless transaction", + "Usage:\n\thf emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" + "\thf emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "activate field and select card."), + arg_lit0("aA", "apdu", "show APDU reqests and responses."), + arg_lit0("tT", "tlv", "TLV decode results."), + arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."), + arg_lit0("fF", "forceaid", "Force search AID. Search AID instead of execute PPSE."), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."), + arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), + arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), + arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool activateField = arg_get_lit(1); + bool showAPDU = arg_get_lit(2); + bool decodeTLV = arg_get_lit(3); + bool paramLoadJSON = arg_get_lit(4); + bool forceSearch = arg_get_lit(5); + + enum TransactionType TrType = TT_MSD; + if (arg_get_lit(6)) + TrType = TT_QVSDCMCHIP; + if (arg_get_lit(7)) + TrType = TT_CDA; + if (arg_get_lit(8)) + TrType = TT_VSDC; + + bool GenACGPO = arg_get_lit(9); + CLIParserFree(); + + SetAPDULogging(showAPDU); + + // init applets list tree + const char *al = "Applets list"; + tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + // Application Selection + // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html + if (!forceSearch) { + // PPSE + PrintAndLog("\n* PPSE."); + SetAPDULogging(showAPDU); + res = EMVSearchPSE(activateField, true, decodeTLV, tlvSelect); + + // check PPSE and select application id + if (!res) { + TLVPrintAIDlistFromSelectTLV(tlvSelect); + EMVSelectApplication(tlvSelect, AID, &AIDlen); + } + } + + // Search + if (!AIDlen) { + PrintAndLog("\n* Search AID in list."); + SetAPDULogging(false); + if (EMVSearch(activateField, true, decodeTLV, tlvSelect)) { + dreturn(2); + } + + // check search and select application id + TLVPrintAIDlistFromSelectTLV(tlvSelect); + EMVSelectApplication(tlvSelect, AID, &AIDlen); + } + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // check if we found EMV application on card + if (!AIDlen) { + PrintAndLog("Can't select AID. EMV AID not found"); + dreturn(2); + } + + // Select + PrintAndLog("\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); + SetAPDULogging(showAPDU); + res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (res) { + PrintAndLog("Can't select AID (%d). Exit...", res); + dreturn(3); + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + PrintAndLog("* Selected."); + + PrintAndLog("\n* Init transaction parameters."); + InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); + TLVPrintFromTLV(tlvRoot); // TODO delete!!! + + PrintAndLog("\n* Calc PDOL."); + pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("ERROR: can't create PDOL TLV."); + dreturn(4); + } + + size_t pdol_data_tlv_data_len; + unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); + if (!pdol_data_tlv_data) { + PrintAndLog("ERROR: can't create PDOL data."); + dreturn(4); + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + + PrintAndLog("\n* GPO."); + res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + free(pdol_data_tlv_data); + //free(pdol_data_tlv); --- free on exit. + + if (res) { + PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + dreturn(5); + } + + // process response template format 1 [id:80 2b AIP + x4b AFL] and format 2 [id:77 TLV] + ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV); + + // extract PAN from track2 + { + const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); + if (!tlvdb_get(tlvRoot, 0x5a, NULL) && track2 && track2->len >= 8) { + struct tlvdb *pan = GetPANFromTrack2(track2); + if (pan) { + tlvdb_add(tlvRoot, pan); + + const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL); + PrintAndLog("\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); + } else { + PrintAndLog("\n* * WARNING: Can't extract PAN from track2."); + } + } + } + + PrintAndLog("\n* Read records from AFL."); + const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); + if (!AFL || !AFL->len) { + PrintAndLog("WARNING: AFL not found."); + } + + while(AFL && AFL->len) { + if (AFL->len % 4) { + PrintAndLog("ERROR: Wrong AFL length: %d", AFL->len); + break; + } + + for (int i = 0; i < AFL->len / 4; i++) { + uint8_t SFI = AFL->value[i * 4 + 0] >> 3; + uint8_t SFIstart = AFL->value[i * 4 + 1]; + uint8_t SFIend = AFL->value[i * 4 + 2]; + uint8_t SFIoffline = AFL->value[i * 4 + 3]; + + PrintAndLog("* * SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { + PrintAndLog("SFI ERROR! Skipped..."); + continue; + } + + for(int n = SFIstart; n <= SFIend; n++) { + PrintAndLog("* * * SFI[%02x] %d", SFI, n); + + res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR SFI[%02x]. APDU error %4x", SFI, sw); + continue; + } + + if (decodeTLV) { + TLVPrintFromBuffer(buf, len); + PrintAndLog(""); + } + + // Build Input list for Offline Data Authentication + // EMV 4.3 book3 10.3, page 96 + if (SFIoffline) { + if (SFI < 11) { + const unsigned char *abuf = buf; + size_t elmlen = len; + struct tlv e; + if (tlv_parse_tl(&abuf, &elmlen, &e)) { + memcpy(&ODAiList[ODAiListLen], &buf[len - elmlen], elmlen); + ODAiListLen += elmlen; + } else { + PrintAndLog("ERROR SFI[%02x]. Creating input list for Offline Data Authentication error.", SFI); + } + } else { + memcpy(&ODAiList[ODAiListLen], buf, len); + ODAiListLen += len; + } + } + } + } + + break; + } + + // copy Input list for Offline Data Authentication + if (ODAiListLen) { + struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag + tlvdb_add(tlvRoot, oda); + PrintAndLog("* Input list for Offline Data Authentication added to TLV. len=%d \n", ODAiListLen); + } + + // get AIP + const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); + uint16_t AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; + PrintAndLog("* * AIP=%04x", AIP); + + // SDA + if (AIP & 0x0040) { + PrintAndLog("\n* SDA"); + trSDA(tlvRoot); + } + + // DDA + if (AIP & 0x0020) { + PrintAndLog("\n* DDA"); + trDDA(decodeTLV, tlvRoot); + } + + // transaction check + + // qVSDC + if (TrType == TT_QVSDCMCHIP|| TrType == TT_CDA){ + // 9F26: Application Cryptogram + const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL); + if (AC) { + PrintAndLog("\n--> qVSDC transaction."); + PrintAndLog("* AC path"); + + // 9F36: Application Transaction Counter (ATC) + const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL); + if (ATC) { + + // 9F10: Issuer Application Data - optional + const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9F10, NULL); + + // print AC data + PrintAndLog("ATC: %s", sprint_hex(ATC->value, ATC->len)); + PrintAndLog("AC: %s", sprint_hex(AC->value, AC->len)); + if (IAD){ + PrintAndLog("IAD: %s", sprint_hex(IAD->value, IAD->len)); + + if (IAD->len >= IAD->value[0] + 1) { + PrintAndLog("\tKey index: 0x%02x", IAD->value[1]); + PrintAndLog("\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); + PrintAndLog("\tCVR:", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); + struct tlvdb * cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]); + TLVPrintFromTLVLev(cvr, 1); + } + } else { + PrintAndLog("WARNING: IAD not found."); + } + + } else { + PrintAndLog("ERROR AC: Application Transaction Counter (ATC) not found."); + } + } + } + + // Mastercard M/CHIP + if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)){ + const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL); + if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag + PrintAndLog("\n--> Mastercard M/Chip transaction."); + + PrintAndLog("* * Generate challenge"); + res = EMVGenerateChallenge(true, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR GetChallenge. APDU error %4x", sw); + dreturn(6); + } + if (len < 4) { + PrintAndLog("ERROR GetChallenge. Wrong challenge length %d", len); + dreturn(6); + } + + // ICC Dynamic Number + struct tlvdb * ICCDynN = tlvdb_fixed(0x9f4c, len, buf); + tlvdb_add(tlvRoot, ICCDynN); + if (decodeTLV){ + PrintAndLog("\n* * ICC Dynamic Number:"); + TLVPrintFromTLV(ICCDynN); + } + + PrintAndLog("* * Calc CDOL1"); + struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag + if (!cdol_data_tlv){ + PrintAndLog("ERROR: can't create CDOL1 TLV."); + dreturn(6); + } + PrintAndLog("CDOL1 data[%d]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + + PrintAndLog("* * AC1"); + // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD + res = EMVAC(true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (res) { + PrintAndLog("AC1 error(%d): %4x. Exit...", res, sw); + dreturn(7); + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + // CDA + PrintAndLog("\n* CDA:"); + struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len); + res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv); + if (res) { + PrintAndLog("CDA error (%d)", res); + } + free(ac_tlv); + free(cdol_data_tlv); + + PrintAndLog("\n* M/Chip transaction result:"); + // 9F27: Cryptogram Information Data (CID) + const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL); + if (CID) { + emv_tag_dump(CID, stdout, 0); + PrintAndLog("------------------------------"); + if (CID->len > 0) { + switch(CID->value[0] & EMVAC_AC_MASK){ + case EMVAC_AAC: + PrintAndLog("Transaction DECLINED."); + break; + case EMVAC_TC: + PrintAndLog("Transaction approved OFFLINE."); + break; + case EMVAC_ARQC: + PrintAndLog("Transaction approved ONLINE."); + break; + default: + PrintAndLog("ERROR: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); + break; + } + } else { + PrintAndLog("ERROR: Wrong CID length %d", CID->len); + } + } else { + PrintAndLog("ERROR: CID(9F27) not found."); + } + + } + } + + // MSD + if (AIP & 0x8000 && TrType == TT_MSD) { + PrintAndLog("\n--> MSD transaction."); + + PrintAndLog("* MSD dCVV path. Check dCVV"); + + const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); + if (track2) { + PrintAndLog("Track2: %s", sprint_hex(track2->value, track2->len)); + + struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2); + PrintAndLog("dCVV raw data:"); + TLVPrintFromTLV(dCVV); + + if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { + PrintAndLog("\n* Mastercard calculate UDOL"); + + // UDOL (9F69) + const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL); + // UDOL(9F69) default: 9F6A (Unpredictable number) 4 bytes + const struct tlv defUDOL = { + .tag = 0x01, + .len = 3, + .value = (uint8_t *)"\x9f\x6a\x04", + }; + if (!UDOL) + PrintAndLog("Use default UDOL."); + + struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag + if (!udol_data_tlv){ + PrintAndLog("ERROR: can't create UDOL TLV."); + dreturn(8); + } + + PrintAndLog("UDOL data[%d]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); + + PrintAndLog("\n* Mastercard compute cryptographic checksum(UDOL)"); + + res = MSCComputeCryptoChecksum(true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("ERROR Compute Crypto Checksum. APDU error %4x", sw); + free(udol_data_tlv); + dreturn(9); + } + + if (decodeTLV) { + TLVPrintFromBuffer(buf, len); + PrintAndLog(""); + } + free(udol_data_tlv); + + } + } else { + PrintAndLog("ERROR MSD: Track2 data not found."); + } + } + + // DropField + DropField(); + + // Destroy TLV's + free(pdol_data_tlv); + tlvdb_free(tlvSelect); + tlvdb_free(tlvRoot); + + PrintAndLog("\n* Transaction completed."); + + return 0; +} + +int CmdHFEMVScan(const char *cmd) { + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + int res; + json_t *root; + json_error_t error; + + CLIParserInit("hf emv scan", + "Scan EMV card and save it contents to a file.", + "It executes EMV contactless transaction and saves result to a file which can be used for emulation\n" + "Usage:\n\thf emv scan -at -> scan MSD transaction mode and show APDU and TLV\n" + "\thf emv scan -c -> scan CDA transaction mode\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("aA", "apdu", "show APDU reqests and responses."), + arg_lit0("tT", "tlv", "TLV decode results."), + arg_lit0("eE", "extract", "Extract TLV elements and fill Application Data"), + arg_lit0("jJ", "jload", "Load transaction parameters from `emv/defparams.json` file."), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip."), + arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)."), + arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standart behavior."), + arg_lit0("gG", "acgpo", "VISA. generate AC from GPO."), + arg_lit0("mM", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"), + arg_str1(NULL, NULL, "output.json", "JSON output file name"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool showAPDU = arg_get_lit(1); + bool decodeTLV = arg_get_lit(2); + bool extractTLVElements = arg_get_lit(3); + bool paramLoadJSON = arg_get_lit(4); + + enum TransactionType TrType = TT_MSD; + if (arg_get_lit(6)) + TrType = TT_QVSDCMCHIP; + if (arg_get_lit(7)) + TrType = TT_CDA; + if (arg_get_lit(8)) + TrType = TT_VSDC; + + bool GenACGPO = arg_get_lit(9); + bool MergeJSON = arg_get_lit(10); + uint8_t relfname[250] ={0}; + char *crelfname = (char *)relfname; + int relfnamelen = 0; + CLIGetStrWithReturn(11, relfname, &relfnamelen); + CLIParserFree(); + + SetAPDULogging(showAPDU); + + // current path + file name + if (!strstr(crelfname, ".json")) + strcat(crelfname, ".json"); + char fname[strlen(get_my_executable_directory()) + strlen(crelfname) + 1]; + strcpy(fname, get_my_executable_directory()); + strcat(fname, crelfname); + + if (MergeJSON) { + root = json_load_file(fname, 0, &error); + if (!root) { + PrintAndLog("ERROR: json error on line %d: %s", error.line, error.text); + return 1; + } + + if (!json_is_object(root)) { + PrintAndLog("ERROR: Invalid json format. root must be an object."); + return 1; + } + } else { + root = json_object(); + } + + // drop field at start + DropField(); + + // iso 14443 select + PrintAndLog("--> GET UID, ATS."); + + iso14a_card_select_t card; + if (Hf14443_4aGetCardData(&card)) { + return 2; + } + + JsonSaveStr(root, "$.File.Created", "proxmark3 `hf emv scan`"); + + JsonSaveStr(root, "$.Card.Communication", "iso14443-4a"); + JsonSaveBufAsHex(root, "$.Card.UID", (uint8_t *)&card.uid, card.uidlen); + JsonSaveHex(root, "$.Card.ATQA", card.atqa[0] + (card.atqa[1] << 2), 2); + JsonSaveHex(root, "$.Card.SAK", card.sak, 0); + JsonSaveBufAsHex(root, "$.Card.ATS", (uint8_t *)card.ats, card.ats_len); + + // init applets list tree + const char *al = "Applets list"; + struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + // EMV PPSE + PrintAndLog("--> PPSE."); + res = EMVSelectPSE(true, true, 2, buf, sizeof(buf), &len, &sw); + + if (!res && sw == 0x9000){ + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + JsonSaveBufAsHex(root, "$.PPSE.AID", (uint8_t *)"2PAY.SYS.DDF01", 14); + + struct tlvdb *fci = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, root, "$.PPSE.FCITemplate", fci); + else + JsonSaveTLVTreeElm(root, "$.PPSE.FCITemplate", fci, true, true, false); + JsonSaveTLVValue(root, "$.Application.KernelID", tlvdb_find_full(fci, 0x9f2a)); + tlvdb_free(fci); + } + + res = EMVSearchPSE(false, true, decodeTLV, tlvSelect); + + // check PPSE and select application id + if (!res) { + TLVPrintAIDlistFromSelectTLV(tlvSelect); + } else { + // EMV SEARCH with AID list + SetAPDULogging(false); + PrintAndLog("--> AID search."); + if (EMVSearch(false, true, decodeTLV, tlvSelect)) { + PrintAndLog("E->Can't found any of EMV AID. Exit..."); + tlvdb_free(tlvSelect); + DropField(); + return 3; + } + + // check search and select application id + TLVPrintAIDlistFromSelectTLV(tlvSelect); + } + + // EMV SELECT application + SetAPDULogging(showAPDU); + EMVSelectApplication(tlvSelect, AID, &AIDlen); + + tlvdb_free(tlvSelect); + + if (!AIDlen) { + PrintAndLog("Can't select AID. EMV AID not found. Exit..."); + DropField(); + return 4; + } + + JsonSaveBufAsHex(root, "$.Application.AID", AID, AIDlen); + + // Init TLV tree + const char *alr = "Root terminal TLV tree"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // EMV SELECT applet + + PrintAndLog("\n-->Selecting AID:%s.", sprint_hex_inrow(AID, AIDlen)); + SetAPDULogging(showAPDU); + res = EMVSelect(false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + + if (res) { + PrintAndLog("E->Can't select AID (%d). Exit...", res); + tlvdb_free(tlvRoot); + DropField(); + return 5; + } + + if (decodeTLV) + TLVPrintFromBuffer(buf, len); + + // save mode + if (tlvdb_get(tlvRoot, 0x9f38, NULL)) { + JsonSaveStr(root, "$.Application.Mode", TransactionTypeStr[TrType]); + } + + struct tlvdb *fci = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, root, "$.Application.FCITemplate", fci); + else + JsonSaveTLVTreeElm(root, "$.Application.FCITemplate", fci, true, true, false); + tlvdb_free(fci); + + // create transaction parameters + PrintAndLog("-->Init transaction parameters."); + InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); + + PrintAndLog("-->Calc PDOL."); + struct tlv *pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); + if (!pdol_data_tlv){ + PrintAndLog("E->Can't create PDOL TLV."); + tlvdb_free(tlvRoot); + DropField(); + return 6; + } + + size_t pdol_data_tlv_data_len; + unsigned char *pdol_data_tlv_data = tlv_encode(pdol_data_tlv, &pdol_data_tlv_data_len); + if (!pdol_data_tlv_data) { + PrintAndLog("E->Can't create PDOL data."); + tlvdb_free(tlvRoot); + DropField(); + return 6; + } + PrintAndLog("PDOL data[%d]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + + PrintAndLog("-->GPO."); + res = EMVGPO(true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); + + free(pdol_data_tlv_data); + free(pdol_data_tlv); + + if (res) { + PrintAndLog("GPO error(%d): %4x. Exit...", res, sw); + tlvdb_free(tlvRoot); + DropField(); + return 7; + } + ProcessGPOResponseFormat1(tlvRoot, buf, len, decodeTLV); + + struct tlvdb *gpofci = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, root, "$.Application.GPO", gpofci); + else + JsonSaveTLVTreeElm(root, "$.Application.GPO", gpofci, true, true, false); + + JsonSaveTLVValue(root, "$.ApplicationData.AIP", tlvdb_find_full(gpofci, 0x82)); + JsonSaveTLVValue(root, "$.ApplicationData.AFL", tlvdb_find_full(gpofci, 0x94)); + + tlvdb_free(gpofci); + + PrintAndLog("-->Read records from AFL."); + const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); + + while(AFL && AFL->len) { + if (AFL->len % 4) { + PrintAndLog("E->Wrong AFL length: %d", AFL->len); + break; + } + + json_t *sfijson = json_path_get(root, "$.Application.Records"); + if (!sfijson) { + json_t *app = json_path_get(root, "$.Application"); + json_object_set_new(app, "Records", json_array()); + + sfijson = json_path_get(root, "$.Application.Records"); + } + if (!json_is_array(sfijson)) { + PrintAndLog("E->Internal logic error. `$.Application.Records` is not an array."); + break; + } + for (int i = 0; i < AFL->len / 4; i++) { + uint8_t SFI = AFL->value[i * 4 + 0] >> 3; + uint8_t SFIstart = AFL->value[i * 4 + 1]; + uint8_t SFIend = AFL->value[i * 4 + 2]; + uint8_t SFIoffline = AFL->value[i * 4 + 3]; + + PrintAndLog("--->SFI[%02x] start:%02x end:%02x offline:%02x", SFI, SFIstart, SFIend, SFIoffline); + if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { + PrintAndLog("SFI ERROR! Skipped..."); + continue; + } + + for(int n = SFIstart; n <= SFIend; n++) { + PrintAndLog("---->SFI[%02x] %d", SFI, n); + + res = EMVReadRecord(true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + PrintAndLog("E->SFI[%02x]. APDU error %4x", SFI, sw); + continue; + } + + if (decodeTLV) { + TLVPrintFromBuffer(buf, len); + PrintAndLog(""); + } + + json_t *jsonelm = json_object(); + json_array_append_new(sfijson, jsonelm); + + JsonSaveHex(jsonelm, "SFI", SFI, 1); + JsonSaveHex(jsonelm, "RecordNum", n, 1); + JsonSaveHex(jsonelm, "Offline", SFIoffline, 1); + + struct tlvdb *rsfi = tlvdb_parse_multi(buf, len); + if (extractTLVElements) + JsonSaveTLVTree(root, jsonelm, "$.Data", rsfi); + else + JsonSaveTLVTreeElm(jsonelm, "$.Data", rsfi, true, true, false); + tlvdb_free(rsfi); + } + } + + break; + } + + // getting certificates + if (tlvdb_get(tlvRoot, 0x90, NULL)) { + PrintAndLog("-->Recovering certificates."); + PKISetStrictExecution(false); + RecoveryCertificates(tlvRoot, root); + PKISetStrictExecution(true); + } + + // free tlv object + tlvdb_free(tlvRoot); + + // DropField + DropField(); + + res = json_dump_file(root, fname, JSON_INDENT(2)); + if (res) { + PrintAndLog("ERROR: can't save the file: %s", fname); + return 200; + } + PrintAndLog("File `%s` saved.", fname); + + // free json object + json_decref(root); + + return 0; +} + +int CmdHFEMVTest(const char *cmd) { + return ExecuteCryptoTests(true); +} + +int CmdHelp(const char *Cmd); +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"exec", CmdHFEMVExec, 0, "Executes EMV contactless transaction."}, + {"pse", CmdHFEMVPPSE, 0, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory."}, + {"search", CmdHFEMVSearch, 0, "Try to select all applets from applets list and print installed applets."}, + {"select", CmdHFEMVSelect, 0, "Select applet."}, + {"gpo", CmdHFEMVGPO, 0, "Execute GetProcessingOptions."}, + {"readrec", CmdHFEMVReadRecord, 0, "Read files from card."}, + {"genac", CmdHFEMVAC, 0, "Generate ApplicationCryptogram."}, + {"challenge", CmdHFEMVGenerateChallenge, 0, "Generate challenge."}, + {"intauth", CmdHFEMVInternalAuthenticate, 0, "Internal authentication."}, + {"scan", CmdHFEMVScan, 0, "Scan EMV card and save it contents to json file for emulator."}, + {"test", CmdHFEMVTest, 0, "Crypto logic test."}, + {NULL, NULL, 0, NULL} +}; + +int CmdHFEMV(const char *Cmd) { + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/data.c b/client/emv/cmdemv.h similarity index 60% rename from client/data.c rename to client/emv/cmdemv.h index 4d7d1e41..b3f76508 100644 --- a/client/data.c +++ b/client/emv/cmdemv.h @@ -1,25 +1,32 @@ //----------------------------------------------------------------------------- -// Copyright (C) 2010 iZsh +// Copyright (C) 2017 Merlok // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Data utilities +// EMV commands //----------------------------------------------------------------------------- +#ifndef CMDEMV_H__ +#define CMDEMV_H__ + +#include +#include +#include #include -#include -#include "data.h" -#include "ui.h" +#include #include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "util.h" +#include "util_posix.h" #include "cmdmain.h" +#include "emvcore.h" +#include "apduinfo.h" -uint8_t* sample_buf; +int CmdHFEMV(const char *Cmd); -void GetFromBigBuf(uint8_t *dest, int bytes, int start_index) -{ - sample_buf = dest; - UsbCommand c = {CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K, {start_index, bytes, 0}}; - SendCommand(&c); -} + +#endif \ No newline at end of file diff --git a/client/emv/crypto.c b/client/emv/crypto.c new file mode 100644 index 00000000..c8ced6d4 --- /dev/null +++ b/client/emv/crypto.c @@ -0,0 +1,179 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "crypto.h" +#include "crypto_backend.h" + +#include + +static struct crypto_backend *crypto_backend; + +static bool crypto_init(void) +{ + if (crypto_backend) + return true; + + crypto_backend = crypto_polarssl_init(); + + if (!crypto_backend) + return false; + + return true; +} + +struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash) +{ + struct crypto_hash *ch; + + if (!crypto_init()) + return NULL; + + ch = crypto_backend->hash_open(hash); + if (ch) + ch->algo = hash; + + return ch; +} + +void crypto_hash_close(struct crypto_hash *ch) +{ + ch->close(ch); +} + +void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len) +{ + ch->write(ch, buf, len); +} + +unsigned char *crypto_hash_read(struct crypto_hash *ch) +{ + return ch->read(ch); +} + +size_t crypto_hash_get_size(const struct crypto_hash *ch) +{ + return ch->get_size(ch); +} + +struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...) +{ + struct crypto_pk *cp; + va_list vl; + + if (!crypto_init()) + return NULL; + + va_start(vl, pk); + cp = crypto_backend->pk_open(pk, vl); + va_end(vl); + + if (cp) + cp->algo = pk; + + return cp; +} + +struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...) +{ + struct crypto_pk *cp; + va_list vl; + + if (!crypto_init()) + return NULL; + + if (!crypto_backend->pk_open_priv) + return NULL; + + va_start(vl, pk); + cp = crypto_backend->pk_open_priv(pk, vl); + va_end(vl); + + if (cp) + cp->algo = pk; + + return cp; +} + +struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...) +{ + struct crypto_pk *cp; + va_list vl; + + if (!crypto_init()) + return NULL; + + if (!crypto_backend->pk_genkey) + return NULL; + + va_start(vl, pk); + cp = crypto_backend->pk_genkey(pk, vl); + va_end(vl); + + if (cp) + cp->algo = pk; + + return cp; +} + +void crypto_pk_close(struct crypto_pk *cp) +{ + cp->close(cp); +} + +unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen) +{ + return cp->encrypt(cp, buf, len, clen); +} + +unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen) +{ + if (!cp->decrypt) { + *clen = 0; + + return NULL; + } + + return cp->decrypt(cp, buf, len, clen); +} + +enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp) +{ + if (!cp) + return PK_INVALID; + + return cp->algo; +} + +size_t crypto_pk_get_nbits(const struct crypto_pk *cp) +{ + if (!cp->get_nbits) + return 0; + + return cp->get_nbits(cp); +} + +unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen) +{ + *plen = 0; + + if (!cp->get_parameter) + return NULL; + + return cp->get_parameter(cp, param, plen); +} diff --git a/client/emv/crypto.h b/client/emv/crypto.h new file mode 100644 index 00000000..940e8b2b --- /dev/null +++ b/client/emv/crypto.h @@ -0,0 +1,48 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef CRYPTO_H +#define CRYPTO_H + +#include +#include + +enum crypto_algo_hash { + HASH_INVALID, + HASH_SHA_1, +}; + +struct crypto_hash *crypto_hash_open(enum crypto_algo_hash hash); +void crypto_hash_close(struct crypto_hash *ch); +void crypto_hash_write(struct crypto_hash *ch, const unsigned char *buf, size_t len); +unsigned char *crypto_hash_read(struct crypto_hash *ch); +size_t crypto_hash_get_size(const struct crypto_hash *ch); + +enum crypto_algo_pk { + PK_INVALID, + PK_RSA, +}; + +struct crypto_pk *crypto_pk_open(enum crypto_algo_pk pk, ...); +struct crypto_pk *crypto_pk_open_priv(enum crypto_algo_pk pk, ...); +struct crypto_pk *crypto_pk_genkey(enum crypto_algo_pk pk, ...); +void crypto_pk_close(struct crypto_pk *cp); +unsigned char *crypto_pk_encrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen); +unsigned char *crypto_pk_decrypt(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen); +enum crypto_algo_pk crypto_pk_get_algo(const struct crypto_pk *cp); +size_t crypto_pk_get_nbits(const struct crypto_pk *cp); +unsigned char *crypto_pk_get_parameter(const struct crypto_pk *cp, unsigned param, size_t *plen); + +#endif diff --git a/client/emv/crypto_backend.h b/client/emv/crypto_backend.h new file mode 100644 index 00000000..a815ae11 --- /dev/null +++ b/client/emv/crypto_backend.h @@ -0,0 +1,50 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef CRYPTO_BACKEND_H +#define CRYPTO_BACKEND_H + +#include "crypto.h" + +#include +#include + +struct crypto_hash { + enum crypto_algo_hash algo; + void (*write)(struct crypto_hash *ch, const unsigned char *buf, size_t len); + unsigned char *(*read)(struct crypto_hash *ch); + void (*close)(struct crypto_hash *ch); + size_t (*get_size)(const struct crypto_hash *ch); +}; + +struct crypto_pk { + enum crypto_algo_pk algo; + unsigned char *(*encrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen); + unsigned char *(*decrypt)(const struct crypto_pk *cp, const unsigned char *buf, size_t len, size_t *clen); + unsigned char *(*get_parameter)(const struct crypto_pk *cp, unsigned param, size_t *plen); + size_t (*get_nbits)(const struct crypto_pk *cp); + void (*close)(struct crypto_pk *cp); +}; + +struct crypto_backend { + struct crypto_hash *(*hash_open)(enum crypto_algo_hash hash); + struct crypto_pk *(*pk_open)(enum crypto_algo_pk pk, va_list vl); + struct crypto_pk *(*pk_open_priv)(enum crypto_algo_pk pk, va_list vl); + struct crypto_pk *(*pk_genkey)(enum crypto_algo_pk pk, va_list vl); +}; + +struct crypto_backend *crypto_polarssl_init(void); + +#endif diff --git a/client/emv/crypto_polarssl.c b/client/emv/crypto_polarssl.c new file mode 100644 index 00000000..5a3db8ea --- /dev/null +++ b/client/emv/crypto_polarssl.c @@ -0,0 +1,355 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * Copyright (C) 2017 Merlok + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "crypto.h" +#include "crypto_backend.h" + +#include +#include +#include + +#include "rsa.h" +#include "sha1.h" + +struct crypto_hash_polarssl { + struct crypto_hash ch; + sha1_context ctx; +}; + +static void crypto_hash_polarssl_close(struct crypto_hash *_ch) +{ + struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch; + + free(ch); +} + +static void crypto_hash_polarssl_write(struct crypto_hash *_ch, const unsigned char *buf, size_t len) +{ + struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch; + + sha1_update(&(ch->ctx), buf, len); +} + +static unsigned char *crypto_hash_polarssl_read(struct crypto_hash *_ch) +{ + struct crypto_hash_polarssl *ch = (struct crypto_hash_polarssl *)_ch; + + static unsigned char sha1sum[20]; + sha1_finish(&(ch->ctx), sha1sum); + return sha1sum; +} + +static size_t crypto_hash_polarssl_get_size(const struct crypto_hash *ch) +{ + if (ch->algo == HASH_SHA_1) + return 20; + else + return 0; +} + +static struct crypto_hash *crypto_hash_polarssl_open(enum crypto_algo_hash hash) +{ + if (hash != HASH_SHA_1) + return NULL; + + struct crypto_hash_polarssl *ch = malloc(sizeof(*ch)); + + sha1_starts(&(ch->ctx)); + + ch->ch.write = crypto_hash_polarssl_write; + ch->ch.read = crypto_hash_polarssl_read; + ch->ch.close = crypto_hash_polarssl_close; + ch->ch.get_size = crypto_hash_polarssl_get_size; + + return &ch->ch; +} + +struct crypto_pk_polarssl { + struct crypto_pk cp; + rsa_context ctx; +}; + +static struct crypto_pk *crypto_pk_polarssl_open_rsa(va_list vl) +{ + struct crypto_pk_polarssl *cp = malloc(sizeof(*cp)); + memset(cp, 0x00, sizeof(*cp)); + + char *mod = va_arg(vl, char *); // N + int modlen = va_arg(vl, size_t); + char *exp = va_arg(vl, char *); // E + int explen = va_arg(vl, size_t); + + rsa_init(&cp->ctx, RSA_PKCS_V15, 0); + + cp->ctx.len = modlen; // size(N) in bytes + mpi_read_binary(&cp->ctx.N, (const unsigned char *)mod, modlen); + mpi_read_binary(&cp->ctx.E, (const unsigned char *)exp, explen); + + int res = rsa_check_pubkey(&cp->ctx); + if(res != 0) { + fprintf(stderr, "PolarSSL public key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen); + free(cp); + return NULL; + } + + return &cp->cp; +} + +static struct crypto_pk *crypto_pk_polarssl_open_priv_rsa(va_list vl) +{ + struct crypto_pk_polarssl *cp = malloc(sizeof(*cp)); + memset(cp, 0x00, sizeof(*cp)); + char *mod = va_arg(vl, char *); + int modlen = va_arg(vl, size_t); + char *exp = va_arg(vl, char *); + int explen = va_arg(vl, size_t); + char *d = va_arg(vl, char *); + int dlen = va_arg(vl, size_t); + char *p = va_arg(vl, char *); + int plen = va_arg(vl, size_t); + char *q = va_arg(vl, char *); + int qlen = va_arg(vl, size_t); + char *dp = va_arg(vl, char *); + int dplen = va_arg(vl, size_t); + char *dq = va_arg(vl, char *); + int dqlen = va_arg(vl, size_t); + // calc QP via Q and P +// char *inv = va_arg(vl, char *); +// int invlen = va_arg(vl, size_t); + + rsa_init(&cp->ctx, RSA_PKCS_V15, 0); + + cp->ctx.len = modlen; // size(N) in bytes + mpi_read_binary(&cp->ctx.N, (const unsigned char *)mod, modlen); + mpi_read_binary(&cp->ctx.E, (const unsigned char *)exp, explen); + + mpi_read_binary(&cp->ctx.D, (const unsigned char *)d, dlen); + mpi_read_binary(&cp->ctx.P, (const unsigned char *)p, plen); + mpi_read_binary(&cp->ctx.Q, (const unsigned char *)q, qlen); + mpi_read_binary(&cp->ctx.DP, (const unsigned char *)dp, dplen); + mpi_read_binary(&cp->ctx.DQ, (const unsigned char *)dq, dqlen); + mpi_inv_mod(&cp->ctx.QP, &cp->ctx.Q, &cp->ctx.P); + + int res = rsa_check_privkey(&cp->ctx); + if(res != 0) { + fprintf(stderr, "PolarSSL private key error res=%x exp=%d mod=%d.\n", res * -1, explen, modlen); + free(cp); + return NULL; + } + + return &cp->cp; +} + +static int myrand(void *rng_state, unsigned char *output, size_t len) { + size_t i; + + if(rng_state != NULL) + rng_state = NULL; + + for( i = 0; i < len; ++i ) + output[i] = rand(); + + return 0; +} + + +static struct crypto_pk *crypto_pk_polarssl_genkey_rsa(va_list vl) +{ + struct crypto_pk_polarssl *cp = malloc(sizeof(*cp)); + memset(cp, 0x00, sizeof(*cp)); + + int transient = va_arg(vl, int); + unsigned int nbits = va_arg(vl, unsigned int); + unsigned int exp = va_arg(vl, unsigned int); + + if (transient) { + } + + int res = rsa_gen_key(&cp->ctx, &myrand, NULL, nbits, exp); + if (res) { + fprintf(stderr, "PolarSSL private key generation error res=%x exp=%d nbits=%d.\n", res * -1, exp, nbits); + free(cp); + return NULL; + } + + return &cp->cp; +} + +static void crypto_pk_polarssl_close(struct crypto_pk *_cp) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + + rsa_free(&cp->ctx); + free(cp); +} + +static unsigned char *crypto_pk_polarssl_encrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + int res; + unsigned char *result; + + *clen = 0; + size_t keylen = mpi_size(&cp->ctx.N); + + result = malloc(keylen); + if (!result) { + printf("RSA encrypt failed. Can't allocate result memory.\n"); + return NULL; + } + + res = rsa_public(&cp->ctx, buf, result); + if(res) { + printf("RSA encrypt failed. Error: %x data len: %zd key len: %zd\n", res * -1, len, keylen); + free(result); + return NULL; + } + + *clen = keylen; + + return result; +} + +static unsigned char *crypto_pk_polarssl_decrypt(const struct crypto_pk *_cp, const unsigned char *buf, size_t len, size_t *clen) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + int res; + unsigned char *result; + + *clen = 0; + size_t keylen = mpi_size(&cp->ctx.N); + + result = malloc(keylen); + if (!result) { + printf("RSA encrypt failed. Can't allocate result memory.\n"); + return NULL; + } + + res = rsa_private(&cp->ctx, buf, result); // CHECK??? + if(res) { + printf("RSA decrypt failed. Error: %x data len: %zd key len: %zd\n", res * -1, len, keylen); + free(result); + return NULL; + } + + *clen = keylen; + + return result; +} + +static size_t crypto_pk_polarssl_get_nbits(const struct crypto_pk *_cp) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + + return cp->ctx.len * 8; +return 0; +} + +static unsigned char *crypto_pk_polarssl_get_parameter(const struct crypto_pk *_cp, unsigned param, size_t *plen) +{ + struct crypto_pk_polarssl *cp = (struct crypto_pk_polarssl *)_cp; + unsigned char *result = NULL; + switch(param){ + // mod + case 0: + *plen = mpi_size(&cp->ctx.N); + result = malloc(*plen); + memset(result, 0x00, *plen); + mpi_write_binary(&cp->ctx.N, result, *plen); + break; + // exp + case 1: + *plen = mpi_size(&cp->ctx.E); + result = malloc(*plen); + memset(result, 0x00, *plen); + mpi_write_binary(&cp->ctx.E, result, *plen); + break; + default: + printf("Error get parameter. Param=%d", param); + break; + } + + return result; +} + +static struct crypto_pk *crypto_pk_polarssl_open(enum crypto_algo_pk pk, va_list vl) +{ + struct crypto_pk *cp; + + if (pk == PK_RSA) + cp = crypto_pk_polarssl_open_rsa(vl); + else + return NULL; + + cp->close = crypto_pk_polarssl_close; + cp->encrypt = crypto_pk_polarssl_encrypt; + cp->get_parameter = crypto_pk_polarssl_get_parameter; + cp->get_nbits = crypto_pk_polarssl_get_nbits; + + return cp; +} + +static struct crypto_pk *crypto_pk_polarssl_open_priv(enum crypto_algo_pk pk, va_list vl) +{ + struct crypto_pk *cp; + + if (pk == PK_RSA) + cp = crypto_pk_polarssl_open_priv_rsa(vl); + else + return NULL; + + cp->close = crypto_pk_polarssl_close; + cp->encrypt = crypto_pk_polarssl_encrypt; + cp->decrypt = crypto_pk_polarssl_decrypt; + cp->get_parameter = crypto_pk_polarssl_get_parameter; + cp->get_nbits = crypto_pk_polarssl_get_nbits; + + return cp; +} + +static struct crypto_pk *crypto_pk_polarssl_genkey(enum crypto_algo_pk pk, va_list vl) +{ + struct crypto_pk *cp; + + if (pk == PK_RSA) + cp = crypto_pk_polarssl_genkey_rsa(vl); + else + return NULL; + + cp->close = crypto_pk_polarssl_close; + cp->encrypt = crypto_pk_polarssl_encrypt; + cp->decrypt = crypto_pk_polarssl_decrypt; + cp->get_parameter = crypto_pk_polarssl_get_parameter; + cp->get_nbits = crypto_pk_polarssl_get_nbits; + + return cp; +} + +static struct crypto_backend crypto_polarssl_backend = { + .hash_open = crypto_hash_polarssl_open, + .pk_open = crypto_pk_polarssl_open, + .pk_open_priv = crypto_pk_polarssl_open_priv, + .pk_genkey = crypto_pk_polarssl_genkey, +}; + +struct crypto_backend *crypto_polarssl_init(void) +{ + return &crypto_polarssl_backend; +} diff --git a/client/emv/defparams.json b/client/emv/defparams.json new file mode 100644 index 00000000..e615d323 --- /dev/null +++ b/client/emv/defparams.json @@ -0,0 +1,58 @@ +[ + { + "name": "Transaction Date", + "tag": "9A", + "value": "00 00 00", + "length": 3, + "hint": "format: YYMMDD" + }, + { + "name": "Transaction Type", + "tag": "9C", + "value": "00", + "length": 1, + "hint": "00: Goods and service, 01: Cash" + }, + { + "name": "Amount, authorized", + "tag": "9F 02", + "value": "00 00 00 00 01 00", + "length": 6, + "hint": "amount (numberic) in cents" + }, + { + "name": "Transaction Currency Code", + "tag": "5F 2A", + "value": "09 80", + "length": 2, + "hint": "USD 840, EUR 978, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999" + }, + { + "name": "Terminal Country Code", + "tag": "9F 1A", + "value": "72 75", + "length": 2, + "hint": "ISO3166: de, en (65 6e), uk(75 6b), ru (72 75), us, ua" + }, + { + "name": "Terminal Transaction Qualifiers (TTQ)", + "tag": "9F 66", + "value": "26 00 00 00", + "length": 4, + "hint": "qVSDC 26 00 00 00, gen AC from GPO 26 80 00 00, MSD 86 00 00 00, VSDC 46 00 00 00" + }, + { + "name": "Unpredictable Number", + "tag": "9F 37", + "value": "01 02 03 04", + "length": 4, + "hint": "4 byte random number" + }, + { + "name": "Unpredictable Number (MSD for UDOL)", + "tag": "9F 6A", + "value": "01 02 03 05", + "length": 4, + "hint": "4 byte random number" + } +] diff --git a/client/emv/dol.c b/client/emv/dol.c new file mode 100644 index 00000000..0fc1c49b --- /dev/null +++ b/client/emv/dol.c @@ -0,0 +1,135 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "emv/dol.h" +#include "emv/tlv.h" + +#include +#include + +static size_t dol_calculate_len(const struct tlv *tlv, size_t data_len) +{ + if (!tlv) + return 0; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + size_t count = 0; + + while (left) { + struct tlv tlv; + if (!tlv_parse_tl(&buf, &left, &tlv)) + return 0; + + count += tlv.len; + + /* Last tag can be of variable length */ + if (tlv.len == 0 && left == 0) + count = data_len; + } + + return count; +} + +struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_tag_t tag) +{ + size_t res_len; + if (!tlv || !(res_len = dol_calculate_len(tlv, 0))) { + struct tlv *res_tlv = malloc(sizeof(*res_tlv)); + + res_tlv->tag = tag; + res_tlv->len = 0; + res_tlv->value = NULL; + + return res_tlv; + } + + struct tlv *res_tlv = malloc(sizeof(*res_tlv) + res_len); + if (!res_tlv) + return NULL; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + unsigned char *res = (unsigned char *)(res_tlv + 1); + size_t pos = 0; + + while (left) { + struct tlv cur_tlv; + if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) { + free(res_tlv); + + return NULL; + } + + const struct tlv *tag_tlv = tlvdb_get(tlvdb, cur_tlv.tag, NULL); + if (!tag_tlv) { + memset(res + pos, 0, cur_tlv.len); + } else if (tag_tlv->len > cur_tlv.len) { + memcpy(res + pos, tag_tlv->value, cur_tlv.len); + } else { + // FIXME: cn data should be padded with 0xFF !!! + memcpy(res + pos, tag_tlv->value, tag_tlv->len); + memset(res + pos + tag_tlv->len, 0, cur_tlv.len - tag_tlv->len); + } + pos += cur_tlv.len; + } + + res_tlv->tag = tag; + res_tlv->len = res_len; + res_tlv->value = res; + + return res_tlv; +} + +struct tlvdb *dol_parse(const struct tlv *tlv, const unsigned char *data, size_t data_len) +{ + if (!tlv) + return NULL; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + size_t res_len = dol_calculate_len(tlv, data_len); + size_t pos = 0; + struct tlvdb *db = NULL; + + if (res_len != data_len) + return NULL; + + while (left) { + struct tlv cur_tlv; + if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) { + tlvdb_free(db); + return NULL; + } + + /* Last tag can be of variable length */ + if (cur_tlv.len == 0 && left == 0) + cur_tlv.len = res_len - pos; + + struct tlvdb *tag_db = tlvdb_fixed(cur_tlv.tag, cur_tlv.len, data + pos); + if (!db) + db = tag_db; + else + tlvdb_add(db, tag_db); + + pos += cur_tlv.len; + } + + return db; +} diff --git a/client/emv/dol.h b/client/emv/dol.h new file mode 100644 index 00000000..9b1b5165 --- /dev/null +++ b/client/emv/dol.h @@ -0,0 +1,25 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef DOL_H +#define DOL_H + +#include "emv/tlv.h" +#include + +struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_tag_t tag); +struct tlvdb *dol_parse(const struct tlv *tlv, const unsigned char *buf, size_t len); + +#endif diff --git a/client/emv/dump.c b/client/emv/dump.c new file mode 100644 index 00000000..2368680a --- /dev/null +++ b/client/emv/dump.c @@ -0,0 +1,60 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "dump.h" +#include +#include + +#define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");} + +void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f) +{ + int i; + + if (!f) + f = stdout; + + for (i = 0; i < len; i ++) + fprintf(f, "%s%02hhX", i ? " " : "", ptr[i]); +} + +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f, int level) +{ + int i, j; + + if (!f) + f = stdout; + + for (i = 0; i < len; i += 16) { + PRINT_INDENT(level); + fprintf(f, "\t%02x:", i); + for (j = 0; j < 16; j++) { + if (i + j < len) + fprintf(f, " %02hhx", ptr[i + j]); + else + fprintf(f, " "); + } + fprintf(f, " |"); + for (j = 0; j < 16 && i + j < len; j++) { + fprintf(f, "%c", (ptr[i+j] >= 0x20 && ptr[i+j] < 0x7f) ? ptr[i+j] : '.' ); + } + fprintf(f, "\n"); + } +} + diff --git a/client/emv/dump.h b/client/emv/dump.h new file mode 100644 index 00000000..a981615a --- /dev/null +++ b/client/emv/dump.h @@ -0,0 +1,26 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef DUMP_H +#define DUMP_H + +#include +#include + +void dump_buffer_simple(const unsigned char *ptr, size_t len, FILE *f); +void dump_buffer(const unsigned char *ptr, size_t len, FILE *f, int level); +void dump_buffer_tab(const unsigned char *ptr, size_t len, FILE *f, int tabs); + +#endif diff --git a/client/emv/emv_pk.c b/client/emv/emv_pk.c new file mode 100644 index 00000000..b577855e --- /dev/null +++ b/client/emv/emv_pk.c @@ -0,0 +1,524 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* For asprintf */ +#define _GNU_SOURCE +#include + +#include "emv_pk.h" +#include "crypto.h" +#include "proxmark3.h" + +#include +#include +#include +#include + +#define BCD(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ + -1) + +#define HEX(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ + ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \ + ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \ + -1) + +#define TOHEX(v) ((v) < 10 ? (v) + '0' : (v) - 10 + 'a') + +static ssize_t emv_pk_read_bin(char *buf, unsigned char *bin, size_t size, size_t *read) +{ + size_t left = size; + char *p = buf; + while (*p && *p == ' ') + p++; + + while (left > 0) { + int c1, c2; + c1 = HEX(*p); + if (c1 == -1) + return -(p - buf); + p++; + c2 = HEX(*p); + if (c2 == -1) + return -(p - buf); + p++; + *bin = (c1 * 16 + c2); + bin ++; + left --; + if (*p == ':') + p++; + else if (read) { + *read = (size - left); + break; + } else if (left == 0) + break; + else + return -(p - buf); + } + + while (*p && *p == ' ') + p++; + + p--; + + return (p - buf); +} + +static ssize_t emv_pk_read_ymv(char *buf, unsigned *ymv) +{ + int i; + unsigned char temp[3]; + char *p = buf; + + *ymv = 0; + + while (*p && *p == ' ') + p++; + + for (i = 0; i < 3; i++) { + int c1, c2; + c1 = BCD(*p); + if (c1 == -1) + return -(p - buf); + p++; + c2 = BCD(*p); + if (c2 == -1) + return -(p - buf); + p++; + temp[i] = (c1 * 16 + c2); + } + + while (*p && *p == ' ') + p++; + + p--; + + if (temp[1] > 0x12 || temp[2] > 0x31) + return -(p - buf); + + *ymv = (temp[0] * 0x10000 + temp[1] * 0x100 + temp[2]); + + return (p - buf); +} + +static ssize_t emv_pk_read_string(char *buf, char *str, size_t size) +{ + char *p = buf; + while (*p && *p == ' ') + p++; + + while (size > 1) { + if (*p == ' ') + break; + else if (*p < 0x20 || *p >= 0x7f) + return -(p - buf); + *str = *p; + p++; + str ++; + size --; + } + + *str = 0; + + while (*p && *p == ' ') + p++; + + p--; + + return (p - buf); +} + + +struct emv_pk *emv_pk_parse_pk(char *buf) +{ + struct emv_pk *r = calloc(1, sizeof(*r)); + ssize_t l; + char temp[10]; + + l = emv_pk_read_bin(buf, r->rid, 5, NULL); + if (l <= 0) + goto out; + buf += l; + + l = emv_pk_read_bin(buf, &r->index, 1, NULL); + if (l <= 0) + goto out; + buf += l; + + l = emv_pk_read_ymv(buf, &r->expire); + if (l <= 0) + goto out; + buf += l; + + l = emv_pk_read_string(buf, temp, sizeof(temp)); + if (l <= 0) + goto out; + buf += l; + + if (!strcmp(temp, "rsa")) + r->pk_algo = PK_RSA; + else + goto out; + + l = emv_pk_read_bin(buf, r->exp, sizeof(r->exp), &r->elen); + if (l <= 0) + goto out; + buf += l; + + r->modulus = malloc(2048/8); + l = emv_pk_read_bin(buf, r->modulus, 2048/8, &r->mlen); + if (l <= 0) + goto out2; + buf += l; + + l = emv_pk_read_string(buf, temp, sizeof(temp)); + if (l <= 0) + goto out2; + buf += l; + + if (!strcmp(temp, "sha1")) + r->hash_algo = HASH_SHA_1; + else + goto out2; + + l = emv_pk_read_bin(buf, r->hash, 20, NULL); + if (l <= 0) + goto out2; + + return r; + +out2: + free(r->modulus); +out: + free(r); + return NULL; +} + +static size_t emv_pk_write_bin(char *out, size_t outlen, const unsigned char *buf, size_t len) +{ + int i; + size_t pos = 0; + + if (len == 0) + return 0; + if (outlen < len * 3) + return 0; + + out[pos++] = TOHEX(buf[0] >> 4); + out[pos++] = TOHEX(buf[0] & 0xf); + for (i = 1; i < len; i++) { + out[pos++] = ':'; + out[pos++] = TOHEX(buf[i] >> 4); + out[pos++] = TOHEX(buf[i] & 0xf); + } + out[pos++] = ' '; + + return pos; +} + +static size_t emv_pk_write_str(char *out, size_t outlen, const char *str) +{ + size_t len = strlen(str); + + if (len == 0) + return 0; + if (outlen < len) + return 0; + + memcpy(out, str, len); + + return len; +} + +char *emv_pk_dump_pk(const struct emv_pk *pk) +{ + size_t outsize = 1024; /* should be enough */ + char *out = malloc(outsize); /* should be enough */ + size_t outpos = 0; + size_t rc; + + if (!out) + return NULL; + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->rid, 5); + if (rc == 0) + goto err; + outpos += rc; + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, &pk->index, 1); + if (rc == 0) + goto err; + outpos += rc; + + if (outpos + 7 > outsize) + goto err; + out[outpos++] = TOHEX((pk->expire >> 20) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 16) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 12) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 8 ) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 4 ) & 0xf); + out[outpos++] = TOHEX((pk->expire >> 0 ) & 0xf); + out[outpos++] = ' '; + + if (pk->pk_algo == PK_RSA) { + rc = emv_pk_write_str(out + outpos, outsize - outpos, "rsa"); + if (rc == 0) + goto err; + outpos += rc; + out[outpos++] = ' '; + } else { + if (outpos + 4 > outsize) + goto err; + out[outpos++] = '?'; + out[outpos++] = '?'; + out[outpos++] = TOHEX(pk->pk_algo >> 4); + out[outpos++] = TOHEX(pk->pk_algo & 0xf); + } + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->exp, pk->elen); + if (rc == 0) + goto err; + outpos += rc; + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->modulus, pk->mlen); + if (rc == 0) + goto err; + outpos += rc; + + if (pk->hash_algo == HASH_SHA_1) { + rc = emv_pk_write_str(out + outpos, outsize - outpos, "sha1"); + if (rc == 0) + goto err; + outpos += rc; + out[outpos++] = ' '; + } else { + if (outpos + 4 > outsize) + goto err; + out[outpos++] = '?'; + out[outpos++] = '?'; + out[outpos++] = TOHEX(pk->pk_algo >> 4); + out[outpos++] = TOHEX(pk->pk_algo & 0xf); + } + + + rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->hash, 20); + if (rc == 0) + goto err; + outpos += rc; + + out[outpos-1] = '\0'; + + return out; + +err: + free(out); + return NULL; +} + +bool emv_pk_verify(const struct emv_pk *pk) +{ + struct crypto_hash *ch = crypto_hash_open(pk->hash_algo); + if (!ch) + return false; + + crypto_hash_write(ch, pk->rid, sizeof(pk->rid)); + crypto_hash_write(ch, &pk->index, 1); + crypto_hash_write(ch, pk->modulus, pk->mlen); + crypto_hash_write(ch, pk->exp, pk->elen); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + return false; + } + + size_t hsize = crypto_hash_get_size(ch); + bool r = hsize && !memcmp(h, pk->hash, hsize) ? true : false; + + crypto_hash_close(ch); + + return r; +} + +struct emv_pk *emv_pk_new(size_t modlen, size_t explen) +{ + struct emv_pk *pk; + + /* Not supported ATM */ + if (explen > 3) + return NULL; + + pk = calloc(1, sizeof(*pk)); + if (!pk) + return NULL; + + pk->mlen = modlen; + pk->elen = explen; + + pk->modulus = calloc(modlen, 1); + if (!pk->modulus) { + free(pk); + pk = NULL; + } + + return pk; +} + +void emv_pk_free(struct emv_pk *pk) +{ + if (!pk) + return; + + free(pk->modulus); + free(pk); +} + +static struct emv_pk *emv_pk_get_ca_pk_from_file(const char *fname, + const unsigned char *rid, + unsigned char idx) +{ + if (!fname) + return NULL; + + FILE *f = fopen(fname, "r"); + if (!f) { + perror("fopen"); + return NULL; + } + + while (!feof(f)) { + char buf[2048]; + if (fgets(buf, sizeof(buf), f) == NULL) + break; + + struct emv_pk *pk = emv_pk_parse_pk(buf); + if (!pk) + continue; + if (memcmp(pk->rid, rid, 5) || pk->index != idx) { + emv_pk_free(pk); + continue; + } + + fclose(f); + + return pk; + } + + fclose(f); + + return NULL; +} + +char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx) +{ + if (!dirname) + dirname = ".";//openemv_config_get_str("capk.dir", NULL); + + if (!dirname) + return NULL; + + char *filename; + int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx_%02hhx.0", + dirname, + rid[0], + rid[1], + rid[2], + rid[3], + rid[4], + idx); + + if (ret <= 0) + return NULL; + + return filename; +} + +char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid) +{ + if (!dirname) + dirname = "."; //openemv_config_get_str("capk.dir", NULL); + + if (!dirname) + return NULL; + + char *filename; + int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx.pks", + dirname, + rid[0], + rid[1], + rid[2], + rid[3], + rid[4]); + + 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; + +/* if (!pk) { + char *fname = emv_pk_get_ca_pk_file(NULL, rid, idx); + if (fname) { + pk = emv_pk_get_ca_pk_from_file(fname, rid, idx); + free(fname); + } + } + + if (!pk) { + char *fname = emv_pk_get_ca_pk_rid_file(NULL, rid); + if (fname) { + pk = emv_pk_get_ca_pk_from_file(fname, rid, idx); + free(fname); + } + } +*/ + if (!pk) { + const char *relfname = "emv/capk.txt"; + + char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; + strcpy(fname, get_my_executable_directory()); + strcat(fname, relfname); + + pk = emv_pk_get_ca_pk_from_file(fname, rid, idx); + } + if (!pk) + return NULL; + + printf("Verifying CA PK for %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx %zd bits...", + pk->rid[0], + pk->rid[1], + pk->rid[2], + pk->rid[3], + pk->rid[4], + pk->index, + pk->mlen * 8); + if (emv_pk_verify(pk)) { + printf("OK\n"); + + return pk; + } + + printf("Failed!\n"); + emv_pk_free(pk); + + return NULL; +} diff --git a/client/emv/emv_pk.h b/client/emv/emv_pk.h new file mode 100644 index 00000000..e291a065 --- /dev/null +++ b/client/emv/emv_pk.h @@ -0,0 +1,48 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef EMV_PK_H +#define EMV_PK_H + +#include +#include + +struct emv_pk { + unsigned char rid[5]; + unsigned char index; + unsigned char serial[3]; + unsigned char pan[10]; + unsigned char hash_algo; + unsigned char pk_algo; + unsigned char hash[20]; + unsigned char exp[3]; + size_t elen; + size_t mlen; + unsigned char *modulus; + unsigned int expire; +}; + +#define EXPIRE(yy, mm, dd) 0x ## yy ## mm ## dd + +struct emv_pk *emv_pk_parse_pk(char *buf); +struct emv_pk *emv_pk_new(size_t modlen, size_t explen); +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); +struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx); +#endif diff --git a/client/emv/emv_pki.c b/client/emv/emv_pki.c new file mode 100644 index 00000000..da102291 --- /dev/null +++ b/client/emv/emv_pki.c @@ -0,0 +1,519 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "emv_pki.h" +#include "crypto.h" +#include "dump.h" +#include "util.h" + +#include +#include +#include +#include + +static bool strictExecution = true; +void PKISetStrictExecution(bool se) { + strictExecution = se; +} + +static const unsigned char empty_tlv_value[] = {}; +static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value}; + +static size_t emv_pki_hash_psn[256] = { 0, 0, 11, 2, 17, 2, }; + +static unsigned char *emv_pki_decode_message(const struct emv_pk *enc_pk, + uint8_t msgtype, + size_t *len, + const struct tlv *cert_tlv, + ... /* A list of tlv pointers, end with NULL */ + ) +{ + struct crypto_pk *kcp; + unsigned char *data; + size_t data_len; + va_list vl; + + if (!enc_pk) + return NULL; + + if (!cert_tlv) { + printf("ERROR: Can't find certificate\n"); + return NULL; + } + + if (cert_tlv->len != enc_pk->mlen) { + printf("ERROR: Certificate length (%zd) not equal key length (%zd)\n", cert_tlv->len, enc_pk->mlen); + return NULL; + } + kcp = crypto_pk_open(enc_pk->pk_algo, + enc_pk->modulus, enc_pk->mlen, + enc_pk->exp, enc_pk->elen); + if (!kcp) + return NULL; + + data = crypto_pk_encrypt(kcp, cert_tlv->value, cert_tlv->len, &data_len); + crypto_pk_close(kcp); + +/* if (true){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + }*/ + + if (data[data_len-1] != 0xbc || data[0] != 0x6a || data[1] != msgtype) { + printf("ERROR: Certificate format\n"); + free(data); + return NULL; + } + + size_t hash_pos = emv_pki_hash_psn[msgtype]; + if (hash_pos == 0 || hash_pos > data_len){ + printf("ERROR: Cant get hash position in the certificate\n"); + free(data); + return NULL; + } + + struct crypto_hash *ch; + ch = crypto_hash_open(data[hash_pos]); + if (!ch) { + printf("ERROR: Cant do hash\n"); + free(data); + return NULL; + } + + size_t hash_len = crypto_hash_get_size(ch); + crypto_hash_write(ch, data + 1, data_len - 2 - hash_len); + + va_start(vl, cert_tlv); + while (true) { + const struct tlv *add_tlv = va_arg(vl, const struct tlv *); + if (!add_tlv) + break; + + crypto_hash_write(ch, add_tlv->value, add_tlv->len); + } + va_end(vl); + + if (memcmp(data + data_len - 1 - hash_len, crypto_hash_read(ch), hash_len)) { + printf("ERROR: Calculated wrong hash\n"); + printf("decoded: %s\n",sprint_hex(data + data_len - 1 - hash_len, hash_len)); + printf("calculated: %s\n",sprint_hex(crypto_hash_read(ch), hash_len)); + + if (strictExecution) { + crypto_hash_close(ch); + free(data); + return NULL; + } + } + + crypto_hash_close(ch); + + *len = data_len - hash_len - 1; + + return data; +} + +static unsigned emv_cn_length(const struct tlv *tlv) +{ + int i; + + for (i = 0; i < tlv->len; i++) { + unsigned char c = tlv->value[i]; + + if (c >> 4 == 0xf) + return 2 * i; + + if ((c & 0xf) == 0xf) + return 2 * i + 1; + } + + return 2 * tlv->len; +} + +static unsigned char emv_cn_get(const struct tlv *tlv, unsigned pos) +{ + if (pos > tlv->len * 2) + return 0xf; + + unsigned char c = tlv->value[pos / 2]; + + if (pos % 2) + return c & 0xf; + else + return c >> 4; +} + +static struct emv_pk *emv_pki_decode_key_ex(const struct emv_pk *enc_pk, + unsigned char msgtype, + const struct tlv *pan_tlv, + const struct tlv *cert_tlv, + const struct tlv *exp_tlv, + const struct tlv *rem_tlv, + const struct tlv *add_tlv, + bool showData + ) +{ + size_t pan_length; + unsigned char *data; + size_t data_len; + size_t pk_len; + + if (!cert_tlv || !exp_tlv || !pan_tlv) + return NULL; + + if (!rem_tlv) + rem_tlv = &empty_tlv; + + if (msgtype == 2) + pan_length = 4; + else if (msgtype == 4) + pan_length = 10; + else { + printf("ERROR: Message type must be 2 or 4\n"); + return NULL; + } + + data = emv_pki_decode_message(enc_pk, msgtype, &data_len, + cert_tlv, + rem_tlv, + exp_tlv, + add_tlv, + NULL); + if (!data || data_len < 11 + pan_length) { + printf("ERROR: Can't decode message\n"); + return NULL; + } + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + /* Perform the rest of checks here */ + + struct tlv pan2_tlv = { + .tag = 0x5a, + .len = pan_length, + .value = &data[2], + }; + unsigned pan_len = emv_cn_length(pan_tlv); + unsigned pan2_len = emv_cn_length(&pan2_tlv); + + if (((msgtype == 2) && (pan2_len < 4 || pan2_len > pan_len)) || + ((msgtype == 4) && (pan2_len != pan_len))) { + printf("ERROR: Invalid PAN lengths\n"); + free(data); + + return NULL; + } + + unsigned i; + for (i = 0; i < pan2_len; i++) + if (emv_cn_get(pan_tlv, i) != emv_cn_get(&pan2_tlv, i)) { + printf("ERROR: PAN data mismatch\n"); + printf("tlv pan=%s\n", sprint_hex(pan_tlv->value, pan_tlv->len)); + printf("cert pan=%s\n", sprint_hex(pan2_tlv.value, pan2_tlv.len)); + free(data); + + return NULL; + } + + pk_len = data[9 + pan_length]; + if (pk_len > data_len - 11 - pan_length + rem_tlv->len) { + printf("ERROR: Invalid pk length\n"); + free(data); + return NULL; + } + + if (exp_tlv->len != data[10 + pan_length]) { + free(data); + return NULL; + } + + struct emv_pk *pk = emv_pk_new(pk_len, exp_tlv->len); + + memcpy(pk->rid, enc_pk->rid, 5); + pk->index = enc_pk->index; + + pk->hash_algo = data[7 + pan_length]; + pk->pk_algo = data[8 + pan_length]; + pk->expire = (data[3 + pan_length] << 16) | (data[2 + pan_length] << 8) | 0x31; + memcpy(pk->serial, data + 4 + pan_length, 3); + memcpy(pk->pan, data + 2, pan_length); + memset(pk->pan + pan_length, 0xff, 10 - pan_length); + + memcpy(pk->modulus, data + 11 + pan_length, + pk_len < data_len - (11 + pan_length) ? + pk_len : + data_len - (11 + pan_length)); + memcpy(pk->modulus + data_len - (11 + pan_length), rem_tlv->value, rem_tlv->len); + memcpy(pk->exp, exp_tlv->value, exp_tlv->len); + + free(data); + + return pk; +} + +static struct emv_pk *emv_pki_decode_key(const struct emv_pk *enc_pk, + unsigned char msgtype, + const struct tlv *pan_tlv, + const struct tlv *cert_tlv, + const struct tlv *exp_tlv, + const struct tlv *rem_tlv, + const struct tlv *add_tlv + ) { + return emv_pki_decode_key_ex(enc_pk, msgtype, pan_tlv, cert_tlv, exp_tlv, rem_tlv, add_tlv, false); +} + +struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db) +{ + return emv_pki_decode_key(pk, 2, + tlvdb_get(db, 0x5a, NULL), + tlvdb_get(db, 0x90, NULL), + tlvdb_get(db, 0x9f32, NULL), + tlvdb_get(db, 0x92, NULL), + NULL); +} + +struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv) +{ + return emv_pki_decode_key(pk, 4, + tlvdb_get(db, 0x5a, NULL), + tlvdb_get(db, 0x9f46, NULL), + tlvdb_get(db, 0x9f47, NULL), + tlvdb_get(db, 0x9f48, NULL), + sda_tlv); +} + +struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db) +{ + return emv_pki_decode_key(pk, 4, + tlvdb_get(db, 0x5a, NULL), + tlvdb_get(db, 0x9f2d, NULL), + tlvdb_get(db, 0x9f2e, NULL), + tlvdb_get(db, 0x9f2f, NULL), + NULL); +} + +struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData) +{ + size_t data_len; + unsigned char *data = emv_pki_decode_message(enc_pk, 3, &data_len, + tlvdb_get(db, 0x93, NULL), + sda_tlv, + NULL); + + if (!data || data_len < 5) + return NULL; + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + struct tlvdb *dac_db = tlvdb_fixed(0x9f45, 2, data+3); + + free(data); + + return dac_db; +} +struct tlvdb *emv_pki_recover_dac(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv) { + return emv_pki_recover_dac_ex(enc_pk, db, sda_tlv, false); +} + +struct tlvdb *emv_pki_recover_idn(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv) { + return emv_pki_recover_idn_ex(enc_pk, db, dyn_tlv, false); +} + +struct tlvdb *emv_pki_recover_idn_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *dyn_tlv, bool showData) +{ + size_t data_len; + unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len, + tlvdb_get(db, 0x9f4b, NULL), + dyn_tlv, + NULL); + + if (!data || data_len < 3) + return NULL; + + if (data[3] < 2 || data[3] > data_len - 3) { + free(data); + return NULL; + } + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + size_t idn_len = data[4]; + if (idn_len > data[3] - 1) { + free(data); + return NULL; + } + + // 9f4c ICC Dynamic Number + struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5); + + free(data); + + return idn_db; +} + +struct tlvdb *emv_pki_recover_atc_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, bool showData) +{ + size_t data_len; + unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len, + tlvdb_get(db, 0x9f4b, NULL), + tlvdb_get(db, 0x9f37, NULL), + tlvdb_get(db, 0x9f02, NULL), + tlvdb_get(db, 0x5f2a, NULL), + tlvdb_get(db, 0x9f69, NULL), + NULL); + + if (!data || data_len < 3) + return NULL; + + if (data[3] < 2 || data[3] > data_len - 3) { + free(data); + return NULL; + } + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + size_t idn_len = data[4]; + if (idn_len > data[3] - 1) { + free(data); + return NULL; + } + + // 9f36 Application Transaction Counter (ATC) + struct tlvdb *atc_db = tlvdb_fixed(0x9f36, idn_len, data + 5); + + free(data); + + return atc_db; +} + +static bool tlv_hash(void *data, const struct tlv *tlv, int level, bool is_leaf) +{ + struct crypto_hash *ch = data; + size_t tag_len; + unsigned char *tag; + + if (tlv_is_constructed(tlv)) + return true; + + if (tlv->tag == 0x9f4b) + return true; + + tag = tlv_encode(tlv, &tag_len); + crypto_hash_write(ch, tag, tag_len); + free(tag); + + return true; +} + +struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db, + const struct tlvdb *this_db, + const struct tlv *pdol_data_tlv, + const struct tlv *crm1_tlv, + const struct tlv *crm2_tlv) +{ + return emv_pki_perform_cda_ex(enc_pk, db, this_db, pdol_data_tlv, crm1_tlv, crm2_tlv, false); +} +struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, + const struct tlvdb *this_db, // AC TLV result + const struct tlv *pdol_data_tlv, // PDOL + const struct tlv *crm1_tlv, // CDOL1 + const struct tlv *crm2_tlv, // CDOL2 + bool showData) +{ + const struct tlv *un_tlv = tlvdb_get(db, 0x9f37, NULL); + const struct tlv *cid_tlv = tlvdb_get(this_db, 0x9f27, NULL); + + if (!un_tlv || !cid_tlv) + return NULL; + + size_t data_len = 0; + unsigned char *data = emv_pki_decode_message(enc_pk, 5, &data_len, + tlvdb_get(this_db, 0x9f4b, NULL), + un_tlv, + NULL); + if (!data || data_len < 3) { + printf("ERROR: can't decode message. len %zd\n", data_len); + return NULL; + } + + if (showData){ + printf("Recovered data:\n"); + dump_buffer(data, data_len, stdout, 0); + } + + if (data[3] < 30 || data[3] > data_len - 4) { + printf("ERROR: Invalid data length\n"); + free(data); + return NULL; + } + + if (!cid_tlv || cid_tlv->len != 1 || cid_tlv->value[0] != data[5 + data[4]]) { + printf("ERROR: CID mismatch\n"); + free(data); + return NULL; + } + + struct crypto_hash *ch; + ch = crypto_hash_open(enc_pk->hash_algo); + if (!ch) { + printf("ERROR: can't create hash\n"); + free(data); + return NULL; + } + + if (pdol_data_tlv) + crypto_hash_write(ch, pdol_data_tlv->value, pdol_data_tlv->len); + if (crm1_tlv) + crypto_hash_write(ch, crm1_tlv->value, crm1_tlv->len); + if (crm2_tlv) + crypto_hash_write(ch, crm2_tlv->value, crm2_tlv->len); + + tlvdb_visit(this_db, tlv_hash, ch, 0); + + if (memcmp(data + 5 + data[4] + 1 + 8, crypto_hash_read(ch), 20)) { + printf("ERROR: calculated hash error\n"); + crypto_hash_close(ch); + free(data); + return NULL; + } + crypto_hash_close(ch); + + size_t idn_len = data[4]; + if (idn_len > data[3] - 1) { + printf("ERROR: Invalid IDN length\n"); + free(data); + return NULL; + } + + struct tlvdb *idn_db = tlvdb_fixed(0x9f4c, idn_len, data + 5); + free(data); + + return idn_db; +} diff --git a/client/emv/emv_pki.h b/client/emv/emv_pki.h new file mode 100644 index 00000000..6fa7b12e --- /dev/null +++ b/client/emv/emv_pki.h @@ -0,0 +1,47 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef EMV_PKI_H +#define EMV_PKI_H + +#include "emv_pk.h" +#include "tlv.h" + +#include + +extern void PKISetStrictExecution(bool se); + +struct emv_pk *emv_pki_recover_issuer_cert(const struct emv_pk *pk, struct tlvdb *db); +struct emv_pk *emv_pki_recover_icc_cert(const struct emv_pk *pk, struct tlvdb *db, const struct tlv *sda_tlv); +struct emv_pk *emv_pki_recover_icc_pe_cert(const struct emv_pk *pk, struct tlvdb *db); + +struct tlvdb *emv_pki_recover_dac(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *sda_tlv); +struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData); +struct tlvdb *emv_pki_recover_idn(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *dyn_tlv); +struct tlvdb *emv_pki_recover_idn_ex(const struct emv_pk *pk, const struct tlvdb *db, const struct tlv *dyn_tlv, bool showData); +struct tlvdb *emv_pki_recover_atc_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, bool showData); +struct tlvdb *emv_pki_perform_cda(const struct emv_pk *enc_pk, const struct tlvdb *db, + const struct tlvdb *this_db, + const struct tlv *pdol_data_tlv, + const struct tlv *crm1_tlv, + const struct tlv *crm2_tlv); +struct tlvdb *emv_pki_perform_cda_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, + const struct tlvdb *this_db, + const struct tlv *pdol_data_tlv, + const struct tlv *crm1_tlv, + const struct tlv *crm2_tlv, + bool showData); + +#endif diff --git a/client/emv/emv_pki_priv.c b/client/emv/emv_pki_priv.c new file mode 100644 index 00000000..d67a8705 --- /dev/null +++ b/client/emv/emv_pki_priv.c @@ -0,0 +1,283 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "emv_pki_priv.h" +#include "crypto.h" + +#include +#include +#include + +struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp, + const unsigned char *rid, unsigned char index, + unsigned int expire, enum crypto_algo_hash hash_algo) +{ + size_t modlen, explen; + unsigned char *mod, *exp; + + if (!rid) + return NULL; + + mod = crypto_pk_get_parameter(cp, 0, &modlen); + exp = crypto_pk_get_parameter(cp, 1, &explen); + + if (!mod || !modlen || !exp || !explen) { + free(mod); + free(exp); + + return NULL; + } + + struct emv_pk *pk = emv_pk_new(modlen, explen); + memcpy(pk->rid, rid, 5); + pk->index = index; + pk->expire = expire; + pk->pk_algo = crypto_pk_get_algo(cp); + pk->hash_algo = hash_algo; + memcpy(pk->modulus, mod, modlen); + memcpy(pk->exp, exp, explen); + + free(mod); + free(exp); + + struct crypto_hash *ch = crypto_hash_open(pk->hash_algo); + if (!ch) { + emv_pk_free(pk); + return false; + } + + crypto_hash_write(ch, pk->rid, sizeof(pk->rid)); + crypto_hash_write(ch, &pk->index, 1); + crypto_hash_write(ch, pk->modulus, pk->mlen); + crypto_hash_write(ch, pk->exp, pk->elen); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + emv_pk_free(pk); + + return NULL; + } + + memcpy(pk->hash, h, crypto_hash_get_size(ch)); + crypto_hash_close(ch); + + return pk; +} + +static struct tlvdb *emv_pki_sign_message(const struct crypto_pk *cp, + tlv_tag_t cert_tag, tlv_tag_t rem_tag, + const unsigned char *msg, size_t msg_len, + ... /* A list of tlv pointers, end with NULL */ + ) +{ + size_t tmp_len = (crypto_pk_get_nbits(cp) + 7) / 8; + unsigned char *tmp = malloc(tmp_len); + if (!tmp) + return NULL; + + // XXX + struct crypto_hash *ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(tmp); + + return NULL; + } + + tmp[0] = 0x6a; + tmp[tmp_len - 1] = 0xbc; + + const unsigned char *rem; + size_t rem_len; + size_t hash_len = crypto_hash_get_size(ch); + size_t part_len = tmp_len - 2 - hash_len; + if (part_len < msg_len) { + memcpy(tmp + 1, msg, part_len); + rem = msg + part_len; + rem_len = msg_len - part_len; + } else { + memcpy(tmp + 1, msg, msg_len); + memset(tmp + 1 + msg_len, 0xbb, part_len - msg_len); + rem = NULL; + rem_len = 0; + } + crypto_hash_write(ch, tmp + 1, part_len); + crypto_hash_write(ch, rem, rem_len); + + va_list vl; + va_start(vl, msg_len); + while (true) { + const struct tlv *add_tlv = va_arg(vl, const struct tlv *); + if (!add_tlv) + break; + + crypto_hash_write(ch, add_tlv->value, add_tlv->len); + } + va_end(vl); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(tmp); + + return NULL; + } + + memcpy(tmp + 1 + part_len, h, hash_len); + crypto_hash_close(ch); + + size_t cert_len; + unsigned char *cert = crypto_pk_decrypt(cp, tmp, tmp_len, &cert_len); + free(tmp); + + if (!cert) + return NULL; + + struct tlvdb *db = tlvdb_fixed(cert_tag, cert_len, cert); + free(cert); + if (!db) + return NULL; + + if (rem) { + struct tlvdb *rdb = tlvdb_fixed(rem_tag, rem_len, rem); + if (!rdb) { + tlvdb_free(db); + + return NULL; + } + tlvdb_add(db, rdb); + } + + return db; +} + +static struct tlvdb *emv_pki_sign_key(const struct crypto_pk *cp, + struct emv_pk *ipk, + unsigned char msgtype, + size_t pan_len, + tlv_tag_t cert_tag, + tlv_tag_t exp_tag, + tlv_tag_t rem_tag, + const struct tlv *add_tlv + ) +{ + unsigned pos = 0; + unsigned char *msg = malloc(1 + pan_len + 2 + 3 + 1 + 1 + 1 + 1 + ipk->mlen); + + if (!msg) + return NULL; + + msg[pos++] = msgtype; + memcpy(msg + pos, ipk->pan, pan_len); pos += pan_len; + msg[pos++] = (ipk->expire >> 8) & 0xff; + msg[pos++] = (ipk->expire >> 16) & 0xff; + memcpy(msg + pos, ipk->serial, 3); pos += 3; + msg[pos++] = ipk->hash_algo; + msg[pos++] = ipk->pk_algo; + msg[pos++] = ipk->mlen; + msg[pos++] = ipk->elen; + memcpy(msg + pos, ipk->modulus, ipk->mlen); + pos += ipk->mlen; + + struct tlvdb *exp_db = tlvdb_fixed(exp_tag, ipk->elen, ipk->exp); + if (!exp_db) { + free(msg); + + return NULL; + } + + struct tlvdb *db = emv_pki_sign_message(cp, + cert_tag, rem_tag, + msg, pos, + tlvdb_get(exp_db, exp_tag, NULL), + add_tlv, + NULL); + free(msg); + if (!db) + return NULL; + + tlvdb_add(db, exp_db); + + return db; +} + +struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk) +{ + return emv_pki_sign_key(cp, issuer_pk, 2, 4, 0x90, 0x9f32, 0x92, NULL); +} + +struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv) +{ + return emv_pki_sign_key(cp, icc_pk, 4, 10, 0x9f46, 0x9f47, 0x9f48, sda_tlv); +} + +struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk) +{ + return emv_pki_sign_key(cp, icc_pe_pk, 4, 10, 0x9f2d, 0x9f2e, 0x9f2f, NULL); +} + +struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv) +{ + unsigned pos = 0; + unsigned char *msg = malloc(1+1+dac_tlv->len); + + if (!msg) + return NULL; + + msg[pos++] = 3; + msg[pos++] = HASH_SHA_1; + memcpy(msg+pos, dac_tlv->value, dac_tlv->len); + pos += dac_tlv->len; + + struct tlvdb *db = emv_pki_sign_message(cp, + 0x93, 0, + msg, pos, + sda_tlv, + NULL); + + free(msg); + + return db; +} + +struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv) +{ + unsigned pos = 0; + unsigned char *msg = malloc(1+1+1+1+idn_tlv->len); + + if (!msg) + return NULL; + + msg[pos++] = 5; + msg[pos++] = HASH_SHA_1; + msg[pos++] = idn_tlv->len + 1; + msg[pos++] = idn_tlv->len; + memcpy(msg+pos, idn_tlv->value, idn_tlv->len); + pos += idn_tlv->len; + + struct tlvdb *db = emv_pki_sign_message(cp, + 0x9f4b, 0, + msg, pos, + dyn_tlv, + NULL); + + free(msg); + + return db; +} diff --git a/client/emv/emv_pki_priv.h b/client/emv/emv_pki_priv.h new file mode 100644 index 00000000..4f1639d5 --- /dev/null +++ b/client/emv/emv_pki_priv.h @@ -0,0 +1,35 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef EMV_PKI_PRIV_H +#define EMV_PKI_PRIV_H + +#include "crypto.h" +#include "emv_pk.h" +#include "tlv.h" + +#include + +struct emv_pk *emv_pki_make_ca(const struct crypto_pk *cp, + const unsigned char *rid, unsigned char index, + unsigned int expire, enum crypto_algo_hash hash_algo); +struct tlvdb *emv_pki_sign_issuer_cert(const struct crypto_pk *cp, struct emv_pk *issuer_pk); +struct tlvdb *emv_pki_sign_icc_cert(const struct crypto_pk *cp, struct emv_pk *icc_pk, const struct tlv *sda_tlv); +struct tlvdb *emv_pki_sign_icc_pe_cert(const struct crypto_pk *cp, struct emv_pk *icc_pe_pk); + +struct tlvdb *emv_pki_sign_dac(const struct crypto_pk *cp, const struct tlv *dac_tlv, const struct tlv *sda_tlv); +struct tlvdb *emv_pki_sign_idn(const struct crypto_pk *cp, const struct tlv *idn_tlv, const struct tlv *dyn_tlv); + +#endif diff --git a/client/emv/emv_tags.c b/client/emv/emv_tags.c new file mode 100644 index 00000000..eed77e76 --- /dev/null +++ b/client/emv/emv_tags.c @@ -0,0 +1,702 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tlv.h" +#include "emv_tags.h" + +#include +#include + +#define PRINT_INDENT(level) {for (int i = 0; i < (level); i++) fprintf(f, "\t");} + +enum emv_tag_t { + EMV_TAG_GENERIC, + EMV_TAG_BITMASK, + EMV_TAG_DOL, + EMV_TAG_CVM_LIST, + EMV_TAG_AFL, + EMV_TAG_STRING, + EMV_TAG_NUMERIC, + EMV_TAG_YYMMDD, + EMV_TAG_CVR, + EMV_TAG_CID, +}; + +struct emv_tag { + tlv_tag_t tag; + char *name; + enum emv_tag_t type; + const void *data; +}; + +struct emv_tag_bit { + unsigned bit; + const char *name; +}; + +#define EMV_BIT(byte, bit) ((byte - 1) * 8 + (8 - bit)) +#define EMV_BIT_FINISH { (~0), NULL } + +static const struct emv_tag_bit EMV_AIP[] = { + { EMV_BIT(1, 7), "SDA supported" }, + { EMV_BIT(1, 6), "DDA supported" }, + { EMV_BIT(1, 5), "Cardholder verification is supported" }, + { EMV_BIT(1, 4), "Terminal risk management is to be performed" }, + { EMV_BIT(1, 3), "Issuer authentication is supported" }, + { EMV_BIT(1, 2), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(1, 1), "CDA supported (Combined Dynamic Data Authentication / Application Cryptogram Generation)" }, + { EMV_BIT(2, 8), "MSD is supported (Magnetic Stripe Data)" }, + { EMV_BIT(2, 7), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 6), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(2, 1), "Reserved for use by the EMV Contactless Specifications" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_AUC[] = { + { EMV_BIT(1, 8), "Valid for domestic cash transactions" }, + { EMV_BIT(1, 7), "Valid for international cash transactions" }, + { EMV_BIT(1, 6), "Valid for domestic goods" }, + { EMV_BIT(1, 5), "Valid for international goods" }, + { EMV_BIT(1, 4), "Valid for domestic services" }, + { EMV_BIT(1, 3), "Valid for international services" }, + { EMV_BIT(1, 2), "Valid for ATMs" }, + { EMV_BIT(1, 1), "Valid at terminals other than ATMs" }, + { EMV_BIT(2, 8), "Domestic cashback allowed" }, + { EMV_BIT(2, 7), "International cashback allowed" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_TVR[] = { + { EMV_BIT(1, 8), "Offline data authentication was not performed" }, + { EMV_BIT(1, 7), "SDA failed" }, + { EMV_BIT(1, 6), "ICC data missing" }, + { EMV_BIT(1, 5), "Card appears on terminal exception file" }, + { EMV_BIT(1, 4), "DDA failed" }, + { EMV_BIT(1, 3), "CDA failed" }, + { EMV_BIT(1, 2), "SDA selected" }, + { EMV_BIT(2, 8), "ICC and terminal have different application versions" }, + { EMV_BIT(2, 7), "Expired application" }, + { EMV_BIT(2, 6), "Application not yet effective" }, + { EMV_BIT(2, 5), "Requested service not allowed for card product" }, + { EMV_BIT(2, 4), "New card" }, + { EMV_BIT(3, 8), "Cardholder verification was not successful" }, + { EMV_BIT(3, 7), "Unrecognised CVM" }, + { EMV_BIT(3, 6), "PIN Try Limit exceeded" }, + { EMV_BIT(3, 5), "PIN entry required and PIN pad not present or not working" }, + { EMV_BIT(3, 4), "PIN entry required, PIN pad present, but PIN was not entered" }, + { EMV_BIT(3, 3), "Online PIN entered" }, + { EMV_BIT(4, 8), "Transaction exceeds floor limit" }, + { EMV_BIT(4, 7), "Lower consecutive offline limit exceeded" }, + { EMV_BIT(4, 6), "Upper consecutive offline limit exceeded" }, + { EMV_BIT(4, 5), "Transaction selected randomly for online processing" }, + { EMV_BIT(4, 4), "Merchant forced transaction online" }, + { EMV_BIT(5, 8), "Default TDOL used" }, + { EMV_BIT(5, 7), "Issuer authentication failed" }, + { EMV_BIT(5, 6), "Script processing failed before final GENERATE AC" }, + { EMV_BIT(5, 5), "Script processing failed after final GENERATE AC" }, + { EMV_BIT(5, 4), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 3), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 2), "Reserved for use by the EMV Contactless Specifications" }, + { EMV_BIT(5, 1), "Reserved for use by the EMV Contactless Specifications" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_CTQ[] = { + { EMV_BIT(1, 8), "Online PIN Required" }, + { EMV_BIT(1, 7), "Signature Required" }, + { EMV_BIT(1, 6), "Go Online if Offline Data Authentication Fails and Reader is online capable" }, + { EMV_BIT(1, 5), "Switch Interface if Offline Data Authentication fails and Reader supports VIS" }, + { EMV_BIT(1, 4), "Go Online if Application Expired" }, + { EMV_BIT(1, 3), "Switch Interface for Cash Transactions" }, + { EMV_BIT(1, 2), "Switch Interface for Cashback Transactions" }, + { EMV_BIT(2, 8), "Consumer Device CVM Performed" }, + { EMV_BIT(2, 7), "Card supports Issuer Update Processing at the POS" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_TTQ[] = { + { EMV_BIT(1, 8), "MSD supported" }, + { EMV_BIT(1, 7), "VSDC supported" }, + { EMV_BIT(1, 6), "qVSDC supported" }, + { EMV_BIT(1, 5), "EMV contact chip supported" }, + { EMV_BIT(1, 4), "Offline-only reader" }, + { EMV_BIT(1, 3), "Online PIN supported" }, + { EMV_BIT(1, 2), "Signature supported" }, + { EMV_BIT(1, 1), "Offline Data Authentication (ODA) for Online Authorizations supported\nWarning!!!! Readers compliant to this specification set TTQ byte 1 bit 1 (this field) to 0b" }, + { EMV_BIT(2, 8), "Online cryptogram required" }, + { EMV_BIT(2, 7), "CVM required" }, + { EMV_BIT(2, 6), "(Contact Chip) Offline PIN supported" }, + { EMV_BIT(3, 8), "Issuer Update Processing supported" }, + { EMV_BIT(3, 7), "Mobile functionality supported (Consumer Device CVM)" }, + EMV_BIT_FINISH, +}; + +static const struct emv_tag_bit EMV_CVR[] = { + // mask 0F 0F F0 0F + { EMV_BIT(1, 4), "CDA Performed" }, + { EMV_BIT(1, 3), "Offline DDA Performed" }, + { EMV_BIT(1, 2), "Issuer Authentication Not Performed" }, + { EMV_BIT(1, 1), "Issuer Authentication performed and Failed" }, + { EMV_BIT(2, 4), "Offline PIN Verification Performed" }, + { EMV_BIT(2, 3), "Offline PIN Verification Performed and PIN Not Successfully Verified" }, + { EMV_BIT(2, 2), "PIN Try Limit Exceeded" }, + { EMV_BIT(2, 1), "Last Online Transaction Not Completed" }, + { EMV_BIT(3, 8), "Lower Offline Transaction Count Limit Exceeded" }, + { EMV_BIT(3, 7), "Upper Offline Transaction Count Limit Exceeded" }, + { EMV_BIT(3, 6), "Lower Cumulative Offline Amount Limit Exceeded" }, + { EMV_BIT(3, 5), "Upper Cumulative Offline Amount Limit Exceeded" }, + { EMV_BIT(4, 4), "Issuer script processing failed on last transaction" }, + { EMV_BIT(4, 3), "Offline data authentication failed on previous transaction and transaction declined offline" }, + { EMV_BIT(4, 2), "Go Online on Next Transaction Was Set" }, + { EMV_BIT(4, 1), "Unable to go Online" }, + EMV_BIT_FINISH, +}; + +// All Data Elements by Tags used in TLV structure (according to the EMV 4.2 Standard ) +// https://www.eftlab.co.uk/index.php/site-map/knowledge-base/145-emv-nfc-tags +// http://dexterous-programmer.blogspot.in/2012/05/emv-tags.html +static const struct emv_tag emv_tags[] = { + // internal + { 0x00 , "Unknown ???" }, + { 0x01 , "", EMV_TAG_STRING }, // string for headers + { 0x02 , "Raw data", }, // data + { 0x20 , "Cardholder Verification Results (CVR)", EMV_TAG_CVR }, // not standard! + { 0x21 , "Input list for Offline Data Authentication" }, // not standard! data for "Offline Data Authentication" come from "read records" command. (EMV book3 10.3) + + // EMV + { 0x41 , "Country code and national data" }, + { 0x42 , "Issuer Identification Number (IIN)" }, + { 0x4f , "Application Dedicated File (ADF) Name" }, + { 0x50 , "Application Label", EMV_TAG_STRING }, + { 0x56 , "Track 1 Data" }, + { 0x57 , "Track 2 Equivalent Data" }, + { 0x5a , "Application Primary Account Number (PAN)" }, + { 0x5f20, "Cardholder Name", EMV_TAG_STRING }, + { 0x5f24, "Application Expiration Date", EMV_TAG_YYMMDD }, + { 0x5f25, "Application Effective Date", EMV_TAG_YYMMDD }, + { 0x5f28, "Issuer Country Code", EMV_TAG_NUMERIC }, + { 0x5f2a, "Transaction Currency Code", EMV_TAG_NUMERIC }, + { 0x5f2d, "Language Preference", EMV_TAG_STRING }, + { 0x5f30, "Service Code", EMV_TAG_NUMERIC }, + { 0x5f34, "Application Primary Account Number (PAN) Sequence Number", EMV_TAG_NUMERIC }, + { 0x61 , "Application Template" }, + { 0x6f , "File Control Information (FCI) Template" }, + { 0x70 , "READ RECORD Response Message Template" }, + { 0x77 , "Response Message Template Format 2" }, + { 0x80 , "Response Message Template Format 1" }, + { 0x82 , "Application Interchange Profile", EMV_TAG_BITMASK, &EMV_AIP }, + { 0x83 , "Command Template" }, + { 0x84 , "Dedicated File (DF) Name" }, + { 0x87 , "Application Priority Indicator" }, + { 0x88 , "Short File Identifier (SFI)" }, + { 0x8a , "Authorisation Response Code" }, + { 0x8c , "Card Risk Management Data Object List 1 (CDOL1)", EMV_TAG_DOL }, + { 0x8d , "Card Risk Management Data Object List 2 (CDOL2)", EMV_TAG_DOL }, + { 0x8e , "Cardholder Verification Method (CVM) List", EMV_TAG_CVM_LIST }, + { 0x8f , "Certification Authority Public Key Index" }, + { 0x90 , "Issuer Public Key Certificate" }, + { 0x91 , "Issuer Authentication Data" }, + { 0x92 , "Issuer Public Key Remainder" }, + { 0x93 , "Signed Static Application Data" }, + { 0x94 , "Application File Locator (AFL)", EMV_TAG_AFL }, + { 0x95 , "Terminal Verification Results" }, + { 0x9a , "Transaction Date", EMV_TAG_YYMMDD }, + { 0x9c , "Transaction Type" }, + { 0x9f02, "Amount, Authorised (Numeric)", EMV_TAG_NUMERIC }, + { 0x9f03, "Amount, Other (Numeric)", EMV_TAG_NUMERIC, }, + { 0x9f06, "Application Identifier (AID), Terminal. ISO 7816-5" }, + { 0x9f07, "Application Usage Control", EMV_TAG_BITMASK, &EMV_AUC }, + { 0x9f08, "Application Version Number" }, + { 0x9f0a, "Application Selection Registered Proprietary Data" }, // https://blog.ul-ts.com/posts/electronic-card-identifier-one-more-step-for-mif-compliance/ + { 0x9f0d, "Issuer Action Code - Default", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f0e, "Issuer Action Code - Denial", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f0f, "Issuer Action Code - Online", EMV_TAG_BITMASK, &EMV_TVR }, + { 0x9f10, "Issuer Application Data" }, + { 0x9f11, "Issuer Code Table Index", EMV_TAG_NUMERIC }, + { 0x9f12, "Application Preferred Name", EMV_TAG_STRING }, + { 0x9f13, "Last Online Application Transaction Counter (ATC) Register" }, + { 0x9f17, "Personal Identification Number (PIN) Try Counter" }, + { 0x9f1a, "Terminal Country Code" }, + { 0x9f1f, "Track 1 Discretionary Data", EMV_TAG_STRING }, + { 0x9f21, "Transaction Time" }, + { 0x9f26, "Application Cryptogram" }, + { 0x9f27, "Cryptogram Information Data", EMV_TAG_CID }, + { 0x9f2a, "Kernel Identifier" }, + { 0x9f2d, "ICC PIN Encipherment Public Key Certificate" }, + { 0x9f2e, "ICC PIN Encipherment Public Key Exponent" }, + { 0x9f2f, "ICC PIN Encipherment Public Key Remainder" }, + { 0x9f32, "Issuer Public Key Exponent" }, + { 0x9f34, "Cardholder Verification Method (CVM) Results" }, + { 0x9f35, "Terminal Type" }, + { 0x9f36, "Application Transaction Counter (ATC)" }, + { 0x9f37, "Unpredictable Number" }, + { 0x9f38, "Processing Options Data Object List (PDOL)", EMV_TAG_DOL }, + { 0x9f42, "Application Currency Code", EMV_TAG_NUMERIC }, + { 0x9f44, "Application Currency Exponent", EMV_TAG_NUMERIC }, + { 0x9f45, "Data Authentication Code" }, + { 0x9f46, "ICC Public Key Certificate" }, + { 0x9f47, "ICC Public Key Exponent" }, + { 0x9f48, "ICC Public Key Remainder" }, + { 0x9f49, "Dynamic Data Authentication Data Object List (DDOL)", EMV_TAG_DOL }, + { 0x9f4a, "Static Data Authentication Tag List" }, + { 0x9f4b, "Signed Dynamic Application Data" }, + { 0x9f4c, "ICC Dynamic Number" }, + { 0x9f4d, "Log Entry" }, + { 0x9f4f, "Log Format", EMV_TAG_DOL }, + { 0x9f60, "CVC3 (Track1)" }, + { 0x9f61, "CVC3 (Track2)" }, + { 0x9f62, "PCVC3(Track1)" }, + { 0x9f63, "PUNATC(Track1)" }, + { 0x9f64, "NATC(Track1)" }, + { 0x9f65, "PCVC3(Track2)" }, + { 0x9f66, "PUNATC(Track2) / Terminal Transaction Qualifiers (TTQ)", EMV_TAG_BITMASK, &EMV_TTQ }, + { 0x9f67, "NATC(Track2) / MSD Offset" }, + { 0x9f68, "Cardholder verification method list (PayPass)" }, + { 0x9f69, "Card Authentication Related Data" }, + { 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC }, + { 0x9f6b, "Track 2 Data" }, + { 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ }, + { 0x9f6e, "Form Factor Indicator" }, + { 0xa5 , "File Control Information (FCI) Proprietary Template" }, + { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data" }, + { 0xdf20, "Issuer Proprietary Bitmap (IPB)" }, +}; + +static int emv_sort_tag(tlv_tag_t tag) +{ + return (int)(tag >= 0x100 ? tag : tag << 8); +} + +static int emv_tlv_compare(const void *a, const void *b) +{ + const struct tlv *tlv = a; + const struct emv_tag *tag = b; + + return emv_sort_tag(tlv->tag) - (emv_sort_tag(tag->tag)); +} + +static const struct emv_tag *emv_get_tag(const struct tlv *tlv) +{ + struct emv_tag *tag = bsearch(tlv, emv_tags, sizeof(emv_tags)/sizeof(emv_tags[0]), + sizeof(emv_tags[0]), emv_tlv_compare); + + return tag ? tag : &emv_tags[0]; +} + +static const char *bitstrings[] = { + ".......1", + "......1.", + ".....1..", + "....1...", + "...1....", + "..1.....", + ".1......", + "1.......", +}; + +static void emv_tag_dump_bitmask(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) +{ + const struct emv_tag_bit *bits = tag->data; + unsigned bit, byte; + + for (byte = 1; byte <= tlv->len; byte ++) { + unsigned char val = tlv->value[byte - 1]; + PRINT_INDENT(level); + fprintf(f, "\tByte %u (%02x)\n", byte, val); + for (bit = 8; bit > 0; bit--, val <<= 1) { + if (val & 0x80){ + PRINT_INDENT(level); + fprintf(f, "\t\t%s - '%s'\n", bitstrings[bit - 1], + bits->bit == EMV_BIT(byte, bit) ? bits->name : "Unknown"); + } + if (bits->bit == EMV_BIT(byte, bit)) + bits ++; + } + } +} + +static void emv_tag_dump_dol(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) +{ + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + + while (left) { + struct tlv doltlv; + const struct emv_tag *doltag; + + if (!tlv_parse_tl(&buf, &left, &doltlv)) { + PRINT_INDENT(level); + fprintf(f, "Invalid Tag-Len\n"); + continue; + } + + doltag = emv_get_tag(&doltlv); + + PRINT_INDENT(level); + fprintf(f, "\tTag %4hx len %02zx ('%s')\n", doltlv.tag, doltlv.len, doltag->name); + } +} + +static void emv_tag_dump_string(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) +{ + fprintf(f, "\tString value '"); + fwrite(tlv->value, 1, tlv->len, f); + fprintf(f, "'\n"); +} + +static unsigned long emv_value_numeric(const struct tlv *tlv, unsigned start, unsigned end) +{ + unsigned long ret = 0; + int i; + + if (end > tlv->len * 2) + return ret; + if (start >= end) + return ret; + + if (start & 1) { + ret += tlv->value[start/2] & 0xf; + i = start + 1; + } else + i = start; + + for (; i < end - 1; i += 2) { + ret *= 10; + ret += tlv->value[i/2] >> 4; + ret *= 10; + ret += tlv->value[i/2] & 0xf; + } + + if (end & 1) { + ret *= 10; + ret += tlv->value[end/2] >> 4; + } + + return ret; +} + +static void emv_tag_dump_numeric(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) +{ + PRINT_INDENT(level); + fprintf(f, "\tNumeric value %lu\n", emv_value_numeric(tlv, 0, tlv->len * 2)); +} + +static void emv_tag_dump_yymmdd(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) +{ + PRINT_INDENT(level); + fprintf(f, "\tDate: 20%02ld.%ld.%ld\n", + emv_value_numeric(tlv, 0, 2), + emv_value_numeric(tlv, 2, 4), + emv_value_numeric(tlv, 4, 6)); +} + +static uint32_t emv_get_binary(const unsigned char *S) +{ + return (S[0] << 24) | (S[1] << 16) | (S[2] << 8) | (S[3] << 0); +} + +// https://github.com/binaryfoo/emv-bertlv/blob/master/src/main/resources/fields/visa-cvr.txt +static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { + if (!tlv || tlv->len < 1) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + if (tlv->len != tlv->value[0] + 1) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID length!\n"); + return; + } + + if (tlv->len >= 2) { + // AC1 + PRINT_INDENT(level); + if ((tlv->value[1] & 0xC0) == 0x00) fprintf(f, "\tAC1: AAC (Transaction declined)\n"); + if ((tlv->value[1] & 0xC0) == 0x40) fprintf(f, "\tAC1: TC (Transaction approved)\n"); + if ((tlv->value[1] & 0xC0) == 0x80) fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n"); + if ((tlv->value[1] & 0xC0) == 0xC0) fprintf(f, "\tAC1: RFU\n"); + // AC2 + PRINT_INDENT(level); + if ((tlv->value[1] & 0x30) == 0x00) fprintf(f, "\tAC2: AAC (Transaction declined)\n"); + if ((tlv->value[1] & 0x30) == 0x10) fprintf(f, "\tAC2: TC (Transaction approved)\n"); + if ((tlv->value[1] & 0x30) == 0x20) fprintf(f, "\tAC2: not requested (ARQC)\n"); + if ((tlv->value[1] & 0x30) == 0x30) fprintf(f, "\tAC2: RFU\n"); + } + if (tlv->len >= 3 && (tlv->value[2] >> 4)) { + PRINT_INDENT(level); + fprintf(f, "\tPIN try: %x\n", tlv->value[2] >> 4); + } + if (tlv->len >= 4 && (tlv->value[3] & 0x0F)) { + PRINT_INDENT(level); + fprintf(f, "\tIssuer discretionary bits: %x\n", tlv->value[3] & 0x0F); + } + if (tlv->len >= 5 && (tlv->value[4] >> 4)) { + PRINT_INDENT(level); + fprintf(f, "\tSuccessfully processed issuer script commands: %x\n", tlv->value[4] >> 4); + } + + // mask 0F 0F F0 0F + uint8_t data[20] = {0}; + memcpy(data, &tlv->value[1], tlv->len - 1); + data[0] &= 0x0F; + data[1] &= 0x0F; + data[2] &= 0xF0; + data[3] &= 0x0F; + const struct tlv bit_tlv = { + .tag = tlv->tag, + .len = tlv->len - 1, + .value = data, + }; + const struct emv_tag bit_tag = { + .tag = tag->tag, + .name = tag->name, + .type = EMV_TAG_BITMASK, + .data = EMV_CVR, + }; + + if (data[0] || data[1] || data[2] || data[3]) + emv_tag_dump_bitmask(&bit_tlv, &bit_tag, f, level); + + return; +} + +// EMV Book 3 +static void emv_tag_dump_cid(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) { + if (!tlv || tlv->len < 1) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + PRINT_INDENT(level); + if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_AAC) fprintf(f, "\tAC1: AAC (Transaction declined)\n"); + if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_TC) fprintf(f, "\tAC1: TC (Transaction approved)\n"); + if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_ARQC) fprintf(f, "\tAC1: ARQC (Online authorisation requested)\n"); + if ((tlv->value[0] & EMVAC_AC_MASK) == EMVAC_AC_MASK) fprintf(f, "\tAC1: RFU\n"); + + if (tlv->value[0] & EMVCID_ADVICE) { + PRINT_INDENT(level); + fprintf(f, "\tAdvice required!\n"); + } + + if (tlv->value[0] & EMVCID_REASON_MASK) { + PRINT_INDENT(level); + fprintf(f, "\tReason/advice/referral code: "); + switch((tlv->value[0] & EMVCID_REASON_MASK)) { + case 0: + fprintf(f, "No information given\n"); + break; + case 1: + fprintf(f, "Service not allowed\n"); + break; + case 2: + fprintf(f, "PIN Try Limit exceeded\n"); + break; + case 3: + fprintf(f, "Issuer authentication failed\n"); + break; + default: + fprintf(f, "\tRFU: %2x\n", (tlv->value[0] & EMVCID_REASON_MASK)); + break; + } + } + + return; +} + +static void emv_tag_dump_cvm_list(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level) +{ + uint32_t X, Y; + int i; + + if (tlv->len < 10 || tlv->len % 2) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + X = emv_get_binary(tlv->value); + Y = emv_get_binary(tlv->value + 4); + + PRINT_INDENT(level); + fprintf(f, "\tX: %d\n", X); + PRINT_INDENT(level); + fprintf(f, "\tY: %d\n", Y); + + for (i = 8; i < tlv->len; i+= 2) { + const char *method; + const char *condition; + + switch (tlv->value[i] & 0x3f) { + case 0x0: + method = "Fail CVM processing"; + break; + case 0x1: + method = "Plaintext PIN verification performed by ICC"; + break; + case 0x2: + method = "Enciphered PIN verified online"; + break; + case 0x3: + method = "Plaintext PIN verification performed by ICC and signature (paper)"; + break; + case 0x4: + method = "Enciphered PIN verification performed by ICC"; + break; + case 0x5: + method = "Enciphered PIN verification performed by ICC and signature (paper)"; + break; + case 0x1e: + method = "Signature (paper)"; + break; + case 0x1f: + method = "No CVM required"; + break; + case 0x3f: + method = "NOT AVAILABLE!"; + break; + default: + method = "Unknown"; + break; + } + + switch (tlv->value[i+1]) { + case 0x00: + condition = "Always"; + break; + case 0x01: + condition = "If unattended cash"; + break; + case 0x02: + condition = "If not unattended cash and not manual cash and not purchase with cashback"; + break; + case 0x03: + condition = "If terminal supports the CVM"; + break; + case 0x04: + condition = "If manual cash"; + break; + case 0x05: + condition = "If purchase with cashback"; + break; + case 0x06: + condition = "If transaction is in the application currency and is under X value"; + break; + case 0x07: + condition = "If transaction is in the application currency and is over X value"; + break; + case 0x08: + condition = "If transaction is in the application currency and is under Y value"; + break; + case 0x09: + condition = "If transaction is in the application currency and is over Y value"; + break; + default: + condition = "Unknown"; + break; + } + + PRINT_INDENT(level); + fprintf(f, "\t%02x %02x: '%s' '%s' and '%s' if this CVM is unsuccessful\n", + tlv->value[i], tlv->value[i+1], + method, condition, (tlv->value[i] & 0x40) ? "continue" : "fail"); + } +} + +static void emv_tag_dump_afl(const struct tlv *tlv, const struct emv_tag *tag, FILE *f, int level){ + if (tlv->len < 4 || tlv->len % 4) { + PRINT_INDENT(level); + fprintf(f, "\tINVALID!\n"); + return; + } + + for (int i = 0; i < tlv->len / 4; i++) { + PRINT_INDENT(level); + fprintf(f, "SFI[%02x] start:%02x end:%02x offline:%02x\n", tlv->value[i * 4 + 0] >> 3, tlv->value[i * 4 + 1], tlv->value[i * 4 + 2], tlv->value[i * 4 + 3]); + } +} + +bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level) +{ + if (!tlv) { + fprintf(f, "NULL\n"); + return false; + } + + const struct emv_tag *tag = emv_get_tag(tlv); + + PRINT_INDENT(level); + fprintf(f, "--%2hx[%02zx] '%s':", tlv->tag, tlv->len, tag->name); + + switch (tag->type) { + case EMV_TAG_GENERIC: + fprintf(f, "\n"); + break; + case EMV_TAG_BITMASK: + fprintf(f, "\n"); + emv_tag_dump_bitmask(tlv, tag, f, level); + break; + case EMV_TAG_DOL: + fprintf(f, "\n"); + emv_tag_dump_dol(tlv, tag, f, level); + break; + case EMV_TAG_CVM_LIST: + fprintf(f, "\n"); + emv_tag_dump_cvm_list(tlv, tag, f, level); + break; + case EMV_TAG_AFL: + fprintf(f, "\n"); + emv_tag_dump_afl(tlv, tag, f, level); + break; + case EMV_TAG_STRING: + emv_tag_dump_string(tlv, tag, f, level); + break; + case EMV_TAG_NUMERIC: + emv_tag_dump_numeric(tlv, tag, f, level); + break; + case EMV_TAG_YYMMDD: + emv_tag_dump_yymmdd(tlv, tag, f, level); + break; + case EMV_TAG_CVR: + fprintf(f, "\n"); + emv_tag_dump_cvr(tlv, tag, f, level); + break; + case EMV_TAG_CID: + fprintf(f, "\n"); + emv_tag_dump_cid(tlv, tag, f, level); + break; + }; + + return true; +} + +char *emv_get_tag_name(const struct tlv *tlv) +{ + static char *defstr = ""; + + if (!tlv) + return defstr; + + const struct emv_tag *tag = emv_get_tag(tlv); + if (tag) + return tag->name; + + return defstr; +} diff --git a/client/emv/emv_tags.h b/client/emv/emv_tags.h new file mode 100644 index 00000000..246fc72d --- /dev/null +++ b/client/emv/emv_tags.h @@ -0,0 +1,36 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef TAGS_H +#define TAGS_H + +#include "tlv.h" +#include + +// AC +# define EMVAC_AC_MASK 0xC0 +# define EMVAC_AAC 0x00 +# define EMVAC_TC 0x40 +# define EMVAC_ARQC 0x80 +# define EMVAC_CDAREQ 0x10 + +// CID +# define EMVCID_ADVICE 0x08 +# define EMVCID_REASON_MASK 0x07 + +bool emv_tag_dump(const struct tlv *tlv, FILE *f, int level); +char *emv_get_tag_name(const struct tlv *tlv); + +#endif diff --git a/client/emv/emvcore.c b/client/emv/emvcore.c new file mode 100644 index 00000000..f9dd0cbe --- /dev/null +++ b/client/emv/emvcore.c @@ -0,0 +1,919 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// EMV core functions +//----------------------------------------------------------------------------- + +#include "emvcore.h" +#include "emvjson.h" + +// Got from here. Thanks) +// https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix +static const char *PSElist [] = { + "325041592E5359532E4444463031", // 2PAY.SYS.DDF01 - Visa Proximity Payment System Environment - PPSE + "315041592E5359532E4444463031" // 1PAY.SYS.DDF01 - Visa Payment System Environment - PSE +}; +//static const size_t PSElistLen = sizeof(PSElist)/sizeof(char*); + +char *TransactionTypeStr[] = { + "MSD", + "VSDC", + "qVCDCMCHIP", + "CDA" +}; + +typedef struct { + enum CardPSVendor vendor; + const char* aid; +} TAIDList; + +static const TAIDList AIDlist [] = { + // Visa International + { CV_VISA, "A00000000305076010"}, // VISA ELO Credit + { CV_VISA, "A0000000031010" }, // VISA Debit/Credit (Classic) + { CV_VISA, "A000000003101001" }, // VISA Credit + { CV_VISA, "A000000003101002" }, // VISA Debit + { CV_VISA, "A0000000032010" }, // VISA Electron + { CV_VISA, "A0000000032020" }, // VISA + { CV_VISA, "A0000000033010" }, // VISA Interlink + { CV_VISA, "A0000000034010" }, // VISA Specific + { CV_VISA, "A0000000035010" }, // VISA Specific + { CV_VISA, "A0000000036010" }, // Domestic Visa Cash Stored Value + { CV_VISA, "A0000000036020" }, // International Visa Cash Stored Value + { CV_VISA, "A0000000038002" }, // VISA Auth, VisaRemAuthen EMV-CAP (DPA) + { CV_VISA, "A0000000038010" }, // VISA Plus + { CV_VISA, "A0000000039010" }, // VISA Loyalty + { CV_VISA, "A000000003999910" }, // VISA Proprietary ATM + // Visa USA + { CV_VISA, "A000000098" }, // Debit Card + { CV_VISA, "A0000000980848" }, // Debit Card + // Mastercard International + { CV_MASTERCARD, "A00000000401" }, // MasterCard PayPass + { CV_MASTERCARD, "A0000000041010" }, // MasterCard Credit + { CV_MASTERCARD, "A00000000410101213" }, // MasterCard Credit + { CV_MASTERCARD, "A00000000410101215" }, // MasterCard Credit + { CV_MASTERCARD, "A0000000042010" }, // MasterCard Specific + { CV_MASTERCARD, "A0000000043010" }, // MasterCard Specific + { CV_MASTERCARD, "A0000000043060" }, // Maestro (Debit) + { CV_MASTERCARD, "A000000004306001" }, // Maestro (Debit) + { CV_MASTERCARD, "A0000000044010" }, // MasterCard Specific + { CV_MASTERCARD, "A0000000045010" }, // MasterCard Specific + { CV_MASTERCARD, "A0000000046000" }, // Cirrus + { CV_MASTERCARD, "A0000000048002" }, // SecureCode Auth EMV-CAP + { CV_MASTERCARD, "A0000000049999" }, // MasterCard PayPass + // American Express + { CV_AMERICANEXPRESS, "A000000025" }, + { CV_AMERICANEXPRESS, "A0000000250000" }, + { CV_AMERICANEXPRESS, "A00000002501" }, + { CV_AMERICANEXPRESS, "A000000025010402" }, + { CV_AMERICANEXPRESS, "A000000025010701" }, + { CV_AMERICANEXPRESS, "A000000025010801" }, + // Groupement des Cartes Bancaires "CB" + { CV_CB, "A0000000421010" }, // Cartes Bancaire EMV Card + { CV_CB, "A0000000422010" }, + { CV_CB, "A0000000423010" }, + { CV_CB, "A0000000424010" }, + { CV_CB, "A0000000425010" }, + // JCB CO., LTD. + { CV_JCB, "A00000006510" }, // JCB + { CV_JCB, "A0000000651010" }, // JCB J Smart Credit + // Other + { CV_OTHER, "A0000001544442" }, // Banricompras Debito - Banrisul - Banco do Estado do Rio Grande do SUL - S.A. + { CV_OTHER, "F0000000030001" }, // BRADESCO + { CV_OTHER, "A0000005241010" }, // RuPay - RuPay + { CV_OTHER, "D5780000021010" } // Bankaxept - Bankaxept +}; +static const size_t AIDlistLen = sizeof(AIDlist)/sizeof(TAIDList); + +static bool APDULogging = false; +void SetAPDULogging(bool logging) { + APDULogging = logging; +} + +enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen) { + char buf[100] = {0}; + if (AIDlen < 1) + return CV_NA; + + hex_to_buffer((uint8_t *)buf, AID, AIDlen, sizeof(buf) - 1, 0, 0, true); + + for(int i = 0; i < AIDlistLen; i ++) { + if (strncmp(AIDlist[i].aid, buf, strlen(AIDlist[i].aid)) == 0){ + return AIDlist[i].vendor; + } + } + + return CV_NA; +} + +static bool print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) { + emv_tag_dump(tlv, stdout, level); + if (is_leaf) { + dump_buffer(tlv->value, tlv->len, stdout, level); + } + + return true; +} + +void TLVPrintFromBuffer(uint8_t *data, int datalen) { + struct tlvdb *t = NULL; + t = tlvdb_parse_multi(data, datalen); + if (t) { + PrintAndLog("-------------------- TLV decoded --------------------"); + + tlvdb_visit(t, print_cb, NULL, 0); + tlvdb_free(t); + } else { + PrintAndLog("TLV ERROR: Can't parse response as TLV tree."); + } +} + +void TLVPrintFromTLVLev(struct tlvdb *tlv, int level) { + if (!tlv) + return; + + tlvdb_visit(tlv, print_cb, NULL, level); +} + +void TLVPrintFromTLV(struct tlvdb *tlv) { + TLVPrintFromTLVLev(tlv, 0); +} + +void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) { + PrintAndLog("|------------------|--------|-------------------------|"); + PrintAndLog("| AID |Priority| Name |"); + PrintAndLog("|------------------|--------|-------------------------|"); + + struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f); + if (!ttmp) + PrintAndLog("| none |"); + + while (ttmp) { + const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL); + const struct tlv *tgName = tlvdb_get_inchild(ttmp, 0x50, NULL); + const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL); + if (!tgAID) + break; + PrintAndLog("|%s| %s |%s|", + sprint_hex_inrow_ex(tgAID->value, tgAID->len, 18), + (tgPrio) ? sprint_hex(tgPrio->value, 1) : " ", + (tgName) ? sprint_ascii_ex(tgName->value, tgName->len, 25) : " "); + + ttmp = tlvdb_find_next(ttmp, 0x6f); + } + + PrintAndLog("|------------------|--------|-------------------------|"); +} + +struct tlvdb *GetPANFromTrack2(const struct tlv *track2) { + char track2Hex[200] = {0}; + uint8_t PAN[100] = {0}; + int PANlen = 0; + char *tmp = track2Hex; + + if (!track2) + return NULL; + + for (int i = 0; i < track2->len; ++i, tmp += 2) + sprintf(tmp, "%02x", (unsigned int)track2->value[i]); + + int posD = strchr(track2Hex, 'd') - track2Hex; + if (posD < 1) + return NULL; + + track2Hex[posD] = 0; + if (strlen(track2Hex) % 2) { + track2Hex[posD] = 'F'; + track2Hex[posD + 1] = '\0'; + } + + param_gethex_to_eol(track2Hex, 0, PAN, sizeof(PAN), &PANlen); + + return tlvdb_fixed(0x5a, PANlen, PAN); +} + +struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2) { + char track2Hex[200] = {0}; + char dCVVHex[100] = {0}; + uint8_t dCVV[100] = {0}; + int dCVVlen = 0; + const int PINlen = 5; // must calculated from 9F67 MSD Offset but i have not seen this tag) + char *tmp = track2Hex; + + if (!track2) + return NULL; + + for (int i = 0; i < track2->len; ++i, tmp += 2) + sprintf(tmp, "%02x", (unsigned int)track2->value[i]); + + int posD = strchr(track2Hex, 'd') - track2Hex; + if (posD < 1) + return NULL; + + memset(dCVVHex, '0', 32); + // ATC + memcpy(dCVVHex + 0, track2Hex + posD + PINlen + 11, 4); + // PAN 5 hex + memcpy(dCVVHex + 4, track2Hex, 5); + // expire date + memcpy(dCVVHex + 9, track2Hex + posD + 1, 4); + // service code + memcpy(dCVVHex + 13, track2Hex + posD + 5, 3); + + param_gethex_to_eol(dCVVHex, 0, dCVV, sizeof(dCVV), &dCVVlen); + + return tlvdb_fixed(0x02, dCVVlen, dCVV); +} + +int EMVExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + uint8_t data[APDU_RES_LEN] = {0}; + + *ResultLen = 0; + if (sw) *sw = 0; + uint16_t isw = 0; + + if (ActivateField) + DropField(); + + // COMPUTE APDU + memcpy(data, &apdu, 5); + if (apdu.data) + memcpy(&data[5], apdu.data, apdu.Lc); + + if (APDULogging) + PrintAndLog(">>>> %s", sprint_hex(data, (IncludeLe?6:5) + apdu.Lc)); + + // 6 byes + data = INS + CLA + P1 + P2 + Lc + + Le(?IncludeLe) + int res = ExchangeAPDU14a(data, (IncludeLe?6:5) + apdu.Lc, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + + if (res) { + return res; + } + + if (APDULogging) + PrintAndLog("<<<< %s", sprint_hex(Result, *ResultLen)); + + *ResultLen -= 2; + isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000) { + if (APDULogging) + PrintAndLog("APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + return 5; + } + + // add to tlv tree + if (tlv) { + struct tlvdb *t = tlvdb_parse_multi(Result, *ResultLen); + tlvdb_add(tlv, t); + } + + return 0; +} + +int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchangeEx(false, LeaveFieldON, apdu, true, Result, MaxResultLen, ResultLen, sw, tlv); +} + +int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchangeEx(ActivateField, LeaveFieldON, (sAPDU){0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, true, Result, MaxResultLen, ResultLen, sw, tlv); +} + +int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { + uint8_t buf[APDU_AID_LEN] = {0}; + *ResultLen = 0; + int len = 0; + int res = 0; + switch (PSENum) { + case 1: + param_gethex_to_eol(PSElist[1], 0, buf, sizeof(buf), &len); + break; + case 2: + param_gethex_to_eol(PSElist[0], 0, buf, sizeof(buf), &len); + break; + default: + return -1; + } + + // select + res = EMVSelect(ActivateField, LeaveFieldON, buf, len, Result, MaxResultLen, ResultLen, sw, NULL); + + return res; +} + +int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { + uint8_t data[APDU_RES_LEN] = {0}; + size_t datalen = 0; + uint16_t sw = 0; + int res; + + // select PPSE + res = EMVSelectPSE(ActivateField, true, 2, data, sizeof(data), &datalen, &sw); + + if (!res){ + struct tlvdb *t = NULL; + t = tlvdb_parse_multi(data, datalen); + if (t) { + int retrycnt = 0; + struct tlvdb *ttmp = tlvdb_find_path(t, (tlv_tag_t[]){0x6f, 0xa5, 0xbf0c, 0x61, 0x00}); + if (!ttmp) + PrintAndLog("PPSE don't have records."); + + while (ttmp) { + const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x4f, NULL); + if (tgAID) { + res = EMVSelect(false, true, (uint8_t *)tgAID->value, tgAID->len, data, sizeof(data), &datalen, &sw, tlv); + + // retry if error and not returned sw error + if (res && res != 5) { + if (++retrycnt < 3){ + continue; + } else { + // card select error, proxmark error + if (res == 1) { + PrintAndLog("Exit..."); + return 1; + } + + retrycnt = 0; + PrintAndLog("Retry failed [%s]. Skiped...", sprint_hex_inrow(tgAID->value, tgAID->len)); + } + + // next element + ttmp = tlvdb_find_next(ttmp, 0x61); + continue; + } + retrycnt = 0; + + // all is ok + if (decodeTLV){ + PrintAndLog("%s:", sprint_hex_inrow(tgAID->value, tgAID->len)); + TLVPrintFromBuffer(data, datalen); + } + } + + ttmp = tlvdb_find_next(ttmp, 0x61); + } + + tlvdb_free(t); + } else { + PrintAndLog("PPSE ERROR: Can't get TLV from response."); + } + } else { + PrintAndLog("PPSE ERROR: Can't select PPSE AID. Error: %d", res); + } + + if(!LeaveFieldON) + DropField(); + + return res; +} + +int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { + uint8_t aidbuf[APDU_AID_LEN] = {0}; + int aidlen = 0; + uint8_t data[APDU_RES_LEN] = {0}; + size_t datalen = 0; + uint16_t sw = 0; + + int res = 0; + int retrycnt = 0; + for(int i = 0; i < AIDlistLen; i ++) { + param_gethex_to_eol(AIDlist[i].aid, 0, aidbuf, sizeof(aidbuf), &aidlen); + res = EMVSelect((i == 0) ? ActivateField : false, (i == AIDlistLen - 1) ? LeaveFieldON : true, aidbuf, aidlen, data, sizeof(data), &datalen, &sw, tlv); + // retry if error and not returned sw error + if (res && res != 5) { + if (++retrycnt < 3){ + i--; + } else { + // card select error, proxmark error + if (res == 1) { + PrintAndLog("Exit..."); + return 1; + } + + retrycnt = 0; + PrintAndLog("Retry failed [%s]. Skiped...", AIDlist[i].aid); + } + continue; + } + retrycnt = 0; + + if (res) + continue; + + if (decodeTLV){ + PrintAndLog("%s:", AIDlist[i].aid); + TLVPrintFromBuffer(data, datalen); + } + } + + return 0; +} + +int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) { + // check priority. 0x00 - highest + int prio = 0xffff; + + *AIDlen = 0; + + struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f); + if (!ttmp) + return 1; + + while (ttmp) { + const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL); + const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL); + + if (!tgAID) + break; + + if (tgPrio) { + int pt = bytes_to_num((uint8_t*)tgPrio->value, (tgPrio->len < 2) ? tgPrio->len : 2); + if (pt < prio) { + prio = pt; + + memcpy(AID, tgAID->value, tgAID->len); + *AIDlen = tgAID->len; + } + } else { + // takes the first application from list wo priority + if (!*AIDlen) { + memcpy(AID, tgAID->value, tgAID->len); + *AIDlen = tgAID->len; + } + } + + ttmp = tlvdb_find_next(ttmp, 0x6f); + } + + return 0; +} + +int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +} + +int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); + if (*sw == 0x6700) { + PrintAndLog(">>> trying to reissue command withouth Le..."); + res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0xb2, SFIrec, (SFI << 3) | 0x04, 0, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); + } + return res; +} + +int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +} + +int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + int res = EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, Result, MaxResultLen, ResultLen, sw, tlv); + if (*sw == 0x6700) { + PrintAndLog(">>> trying to reissue command withouth Le..."); + res = EMVExchangeEx(false, LeaveFieldON, (sAPDU){0x00, 0x84, 0x00, 0x00, 0x00, NULL}, false, Result, MaxResultLen, ResultLen, sw, tlv); + } + return res; +} + +int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(LeaveFieldON, (sAPDU){0x00, 0x88, 0x00, 0x00, DDOLLen, DDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +} + +int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchange(LeaveFieldON, (sAPDU){0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv); +} + +// Authentication +static struct emv_pk *get_ca_pk(struct tlvdb *db) { + const struct tlv *df_tlv = tlvdb_get(db, 0x84, NULL); + const struct tlv *caidx_tlv = tlvdb_get(db, 0x8f, NULL); + + if (!df_tlv || !caidx_tlv || df_tlv->len < 6 || caidx_tlv->len != 1) + return NULL; + + PrintAndLog("CA public key index 0x%0x", caidx_tlv->value[0]); + return emv_pk_get_ca_pk(df_tlv->value, caidx_tlv->value[0]); +} + +int trSDA(struct tlvdb *tlv) { + + struct emv_pk *pk = get_ca_pk(tlv); + if (!pk) { + PrintAndLog("ERROR: Key not found. Exit."); + return 2; + } + + struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv); + if (!issuer_pk) { + emv_pk_free(pk); + PrintAndLog("ERROR: Issuer certificate not found. Exit."); + return 2; + } + + PrintAndLog("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx", + issuer_pk->rid[0], + issuer_pk->rid[1], + issuer_pk->rid[2], + issuer_pk->rid[3], + issuer_pk->rid[4], + issuer_pk->index, + issuer_pk->serial[0], + issuer_pk->serial[1], + issuer_pk->serial[2] + ); + + const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL); + if (!sda_tlv || sda_tlv->len < 1) { + emv_pk_free(issuer_pk); + emv_pk_free(pk); + PrintAndLog("ERROR: Can't find input list for Offline Data Authentication. Exit."); + return 3; + } + + struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); + if (dac_db) { + const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL); + PrintAndLog("SDA verified OK. (%02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]); + tlvdb_add(tlv, dac_db); + } else { + emv_pk_free(issuer_pk); + emv_pk_free(pk); + PrintAndLog("ERROR: SSAD verify error"); + return 4; + } + + emv_pk_free(issuer_pk); + emv_pk_free(pk); + return 0; +} + +static const unsigned char default_ddol_value[] = {0x9f, 0x37, 0x04}; +static struct tlv default_ddol_tlv = {.tag = 0x9f49, .len = 3, .value = default_ddol_value }; + +int trDDA(bool decodeTLV, struct tlvdb *tlv) { + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + + struct emv_pk *pk = get_ca_pk(tlv); + if (!pk) { + PrintAndLog("ERROR: Key not found. Exit."); + return 2; + } + + const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL); + if (!sda_tlv || sda_tlv->len < 1) { + emv_pk_free(pk); + PrintAndLog("ERROR: Can't find input list for Offline Data Authentication. Exit."); + return 3; + } + + struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv); + if (!issuer_pk) { + emv_pk_free(pk); + PrintAndLog("ERROR: Issuer certificate not found. Exit."); + return 2; + } + printf("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + issuer_pk->rid[0], + issuer_pk->rid[1], + issuer_pk->rid[2], + issuer_pk->rid[3], + issuer_pk->rid[4], + issuer_pk->index, + issuer_pk->serial[0], + issuer_pk->serial[1], + issuer_pk->serial[2] + ); + + struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlv, sda_tlv); + if (!icc_pk) { + emv_pk_free(pk); + emv_pk_free(issuer_pk); + PrintAndLog("ERROR: ICC setrificate not found. Exit."); + return 2; + } + printf("ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + icc_pk->rid[0], + icc_pk->rid[1], + icc_pk->rid[2], + icc_pk->rid[3], + icc_pk->rid[4], + icc_pk->index, + icc_pk->serial[0], + icc_pk->serial[1], + icc_pk->serial[2] + ); + + struct emv_pk *icc_pe_pk = emv_pki_recover_icc_pe_cert(issuer_pk, tlv); + if (!icc_pe_pk) { + PrintAndLog("WARNING: ICC PE PK recover error. "); + } else { + printf("ICC PE PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + icc_pe_pk->rid[0], + icc_pe_pk->rid[1], + icc_pe_pk->rid[2], + icc_pe_pk->rid[3], + icc_pe_pk->rid[4], + icc_pe_pk->index, + icc_pe_pk->serial[0], + icc_pe_pk->serial[1], + icc_pe_pk->serial[2] + ); + } + + // 9F4B: Signed Dynamic Application Data + const struct tlv *sdad_tlv = tlvdb_get(tlv, 0x9f4b, NULL); + // DDA with internal authenticate OR fDDA with filled 0x9F4B tag (GPO result) + // EMV kernel3 v2.4, contactless book C-3, C.1., page 147 + if (sdad_tlv) { + PrintAndLog("\n* * Got Signed Dynamic Application Data (9F4B) form GPO. Maybe fDDA..."); + + const struct tlvdb *atc_db = emv_pki_recover_atc_ex(icc_pk, tlv, true); + if (!atc_db) { + PrintAndLog("ERROR: Can't recover IDN (ICC Dynamic Number)"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 8; + } + + // 9f36 Application Transaction Counter (ATC) + const struct tlv *atc_tlv = tlvdb_get(atc_db, 0x9f36, NULL); + if(atc_tlv) { + PrintAndLog("\nATC (Application Transaction Counter) [%zu] %s", atc_tlv->len, sprint_hex_inrow(atc_tlv->value, atc_tlv->len)); + + const struct tlv *core_atc_tlv = tlvdb_get(tlv, 0x9f36, NULL); + if(tlv_equal(core_atc_tlv, atc_tlv)) { + PrintAndLog("ATC check OK."); + PrintAndLog("fDDA (fast DDA) verified OK."); + } else { + PrintAndLog("ERROR: fDDA verified, but ATC in the certificate and ATC in the record not the same."); + } + } else { + PrintAndLog("\nERROR: fDDA (fast DDA) verify error"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 9; + } + } else { + struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); + if (dac_db) { + const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL); + printf("SDA verified OK. (%02hhx:%02hhx)\n", dac_tlv->value[0], dac_tlv->value[1]); + tlvdb_add(tlv, dac_db); + } else { + PrintAndLog("ERROR: SSAD verify error"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 4; + } + + PrintAndLog("\n* Calc DDOL"); + const struct tlv *ddol_tlv = tlvdb_get(tlv, 0x9f49, NULL); + if (!ddol_tlv) { + ddol_tlv = &default_ddol_tlv; + PrintAndLog("DDOL [9f49] not found. Using default DDOL"); + } + + struct tlv *ddol_data_tlv = dol_process(ddol_tlv, tlv, 0); + if (!ddol_data_tlv) { + PrintAndLog("ERROR: Can't create DDOL TLV"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 5; + } + + PrintAndLog("DDOL data[%d]: %s", ddol_data_tlv->len, sprint_hex(ddol_data_tlv->value, ddol_data_tlv->len)); + + PrintAndLog("\n* Internal Authenticate"); + int res = EMVInternalAuthenticate(true, (uint8_t *)ddol_data_tlv->value, ddol_data_tlv->len, buf, sizeof(buf), &len, &sw, NULL); + if (res) { + PrintAndLog("Internal Authenticate error(%d): %4x. Exit...", res, sw); + free(ddol_data_tlv); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 6; + } + + struct tlvdb *dda_db = NULL; + if (buf[0] == 0x80) { + if (len < 3 ) { + PrintAndLog("ERROR: Internal Authenticate format1 parsing error. length=%d", len); + } else { + // 9f4b Signed Dynamic Application Data + dda_db = tlvdb_fixed(0x9f4b, len - 2, buf + 2); + tlvdb_add(tlv, dda_db); + if (decodeTLV){ + PrintAndLog("* * Decode response format 1:"); + TLVPrintFromTLV(dda_db); + } + } + } else { + dda_db = tlvdb_parse_multi(buf, len); + if(!dda_db) { + PrintAndLog("ERROR: Can't parse Internal Authenticate result as TLV"); + free(ddol_data_tlv); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 7; + } + tlvdb_add(tlv, dda_db); + + if (decodeTLV) + TLVPrintFromTLV(dda_db); + } + + struct tlvdb *idn_db = emv_pki_recover_idn_ex(icc_pk, dda_db, ddol_data_tlv, true); + free(ddol_data_tlv); + if (!idn_db) { + PrintAndLog("ERROR: Can't recover IDN (ICC Dynamic Number)"); + tlvdb_free(dda_db); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 8; + } + tlvdb_free(dda_db); + + // 9f4c ICC Dynamic Number + const struct tlv *idn_tlv = tlvdb_get(idn_db, 0x9f4c, NULL); + if(idn_tlv) { + PrintAndLog("\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len)); + PrintAndLog("DDA verified OK."); + tlvdb_add(tlv, idn_db); + tlvdb_free(idn_db); + } else { + PrintAndLog("\nERROR: DDA verify error"); + tlvdb_free(idn_db); + + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 9; + } + } + + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 0; +} + +int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv) { + + struct emv_pk *pk = get_ca_pk(tlv); + if (!pk) { + PrintAndLog("ERROR: Key not found. Exit."); + return 2; + } + + const struct tlv *sda_tlv = tlvdb_get(tlv, 0x21, NULL); + if (!sda_tlv || sda_tlv->len < 1) { + PrintAndLog("ERROR: Can't find input list for Offline Data Authentication. Exit."); + emv_pk_free(pk); + return 3; + } + + struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlv); + if (!issuer_pk) { + PrintAndLog("ERROR: Issuer certificate not found. Exit."); + emv_pk_free(pk); + return 2; + } + printf("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + issuer_pk->rid[0], + issuer_pk->rid[1], + issuer_pk->rid[2], + issuer_pk->rid[3], + issuer_pk->rid[4], + issuer_pk->index, + issuer_pk->serial[0], + issuer_pk->serial[1], + issuer_pk->serial[2] + ); + + struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlv, sda_tlv); + if (!icc_pk) { + PrintAndLog("ERROR: ICC setrificate not found. Exit."); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + return 2; + } + printf("ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + icc_pk->rid[0], + icc_pk->rid[1], + icc_pk->rid[2], + icc_pk->rid[3], + icc_pk->rid[4], + icc_pk->index, + icc_pk->serial[0], + icc_pk->serial[1], + icc_pk->serial[2] + ); + + struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); + if (dac_db) { + const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL); + PrintAndLog("SSAD verified OK. (%02hhx:%02hhx)", dac_tlv->value[0], dac_tlv->value[1]); + tlvdb_add(tlv, dac_db); + } else { + PrintAndLog("ERROR: SSAD verify error"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 4; + } + + PrintAndLog("\n* * Check Signed Dynamic Application Data (SDAD)"); + struct tlvdb *idn_db = emv_pki_perform_cda_ex(icc_pk, tlv, ac_tlv, + pdol_data_tlv, // pdol + ac_data_tlv, // cdol1 + NULL, // cdol2 + true); + if (idn_db) { + const struct tlv *idn_tlv = tlvdb_get(idn_db, 0x9f4c, NULL); + PrintAndLog("\nIDN (ICC Dynamic Number) [%zu] %s", idn_tlv->len, sprint_hex_inrow(idn_tlv->value, idn_tlv->len)); + PrintAndLog("CDA verified OK."); + tlvdb_add(tlv, idn_db); + } else { + PrintAndLog("\nERROR: CDA verify error"); + } + + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 0; +} + +int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root) { + + struct emv_pk *pk = get_ca_pk(tlvRoot); + if (!pk) { + PrintAndLog("ERROR: Key not found. Exit."); + return 1; + } + + struct emv_pk *issuer_pk = emv_pki_recover_issuer_cert(pk, tlvRoot); + if (!issuer_pk) { + emv_pk_free(pk); + PrintAndLog("WARNING: Issuer certificate not found. Exit."); + return 2; + } + PrintAndLog("Issuer PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx", + issuer_pk->rid[0], + issuer_pk->rid[1], + issuer_pk->rid[2], + issuer_pk->rid[3], + issuer_pk->rid[4], + issuer_pk->index, + issuer_pk->serial[0], + issuer_pk->serial[1], + issuer_pk->serial[2] + ); + + JsonSaveBufAsHex(root, "$.ApplicationData.RID", issuer_pk->rid, 5); + + char *issuer_pk_c = emv_pk_dump_pk(issuer_pk); + JsonSaveStr(root, "$.ApplicationData.IssuerPublicKeyDec", issuer_pk_c); + JsonSaveBufAsHex(root, "$.ApplicationData.IssuerPublicKeyModulus", issuer_pk->modulus, issuer_pk->mlen); + free(issuer_pk_c); + + struct emv_pk *icc_pk = emv_pki_recover_icc_cert(issuer_pk, tlvRoot, NULL); + if (!icc_pk) { + emv_pk_free(pk); + emv_pk_free(issuer_pk); + PrintAndLog("WARNING: ICC certificate not found. Exit."); + return 2; + } + printf("ICC PK recovered. RID %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx CSN %02hhx:%02hhx:%02hhx\n", + icc_pk->rid[0], + icc_pk->rid[1], + icc_pk->rid[2], + icc_pk->rid[3], + icc_pk->rid[4], + icc_pk->index, + icc_pk->serial[0], + icc_pk->serial[1], + icc_pk->serial[2] + ); + + char *icc_pk_c = emv_pk_dump_pk(icc_pk); + JsonSaveStr(root, "$.ApplicationData.ICCPublicKeyDec", icc_pk_c); + JsonSaveBufAsHex(root, "$.ApplicationData.ICCPublicKeyModulus", icc_pk->modulus, icc_pk->mlen); + free(issuer_pk_c); + + return 0; +} diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h new file mode 100644 index 00000000..ece7324a --- /dev/null +++ b/client/emv/emvcore.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// EMV core functionality +//----------------------------------------------------------------------------- + +#ifndef EMVCORE_H__ +#define EMVCORE_H__ + +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "common.h" +#include "ui.h" +#include "cmdhf14a.h" +#include "apduinfo.h" +#include "tlv.h" +#include "dol.h" +#include "dump.h" +#include "emv_tags.h" +#include "emv_pk.h" +#include "emv_pki.h" + +#define APDU_RES_LEN 260 +#define APDU_AID_LEN 50 + +enum TransactionType { + TT_MSD, + TT_VSDC, // not standart for contactless!!!! + TT_QVSDCMCHIP, + TT_CDA, +}; +extern char *TransactionTypeStr[]; + +typedef struct { + uint8_t CLA; + uint8_t INS; + uint8_t P1; + uint8_t P2; + uint8_t Lc; + uint8_t *data; +} sAPDU; + +enum CardPSVendor { + CV_NA, + CV_VISA, + CV_MASTERCARD, + CV_AMERICANEXPRESS, + CV_JCB, + CV_CB, + CV_OTHER, +}; +extern enum CardPSVendor GetCardPSVendor(uint8_t * AID, size_t AIDlen); + +extern void TLVPrintFromBuffer(uint8_t *data, int datalen); +extern void TLVPrintFromTLV(struct tlvdb *tlv); +extern void TLVPrintFromTLVLev(struct tlvdb *tlv, int level); +extern void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv); + +extern struct tlvdb *GetPANFromTrack2(const struct tlv *track2); +extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2); + +extern void SetAPDULogging(bool logging); + +// search application +extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); +extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); +extern int EMVSelectPSE(bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); +extern int EMVSelect(bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// select application +extern int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen); +// Get Processing Options +extern int EMVGPO(bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVReadRecord(bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// AC +extern int EMVGenerateChallenge(bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +extern int EMVAC(bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// DDA +extern int EMVInternalAuthenticate(bool LeaveFieldON, uint8_t *DDOL, size_t DDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// Mastercard +int MSCComputeCryptoChecksum(bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); +// Auth +extern int trSDA(struct tlvdb *tlv); +extern int trDDA(bool decodeTLV, struct tlvdb *tlv); +extern int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, struct tlv *ac_data_tlv); + +extern int RecoveryCertificates(struct tlvdb *tlvRoot, json_t *root); + +#endif + + + + diff --git a/client/emv/emvjson.c b/client/emv/emvjson.c new file mode 100644 index 00000000..02297435 --- /dev/null +++ b/client/emv/emvjson.c @@ -0,0 +1,355 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// EMV json logic +//----------------------------------------------------------------------------- + +#include "emvjson.h" +#include +#include +#include +#include +#include +#include "util.h" +#include "ui.h" +#include "proxmark3.h" +#include "emv_tags.h" + +static const ApplicationDataElm ApplicationData[] = { +{0x82, "AIP"}, +{0x94, "AFL"}, + +{0x5A, "PAN"}, +{0x5F34, "PANSeqNo"}, +{0x5F24, "ExpirationDate"}, +{0x5F25, "EffectiveDate"}, +{0x5F28, "IssuerCountryCode"}, + +{0x50, "ApplicationLabel"}, +{0x9F08, "VersionNumber"}, +{0x9F42, "CurrencyCode"}, +{0x5F2D, "LanguagePreference"}, +{0x87, "PriorityIndicator"}, +{0x9F36, "ATC"}, //Application Transaction Counter + +{0x5F20, "CardholderName"}, + +{0x9F38, "PDOL"}, +{0x8C, "CDOL1"}, +{0x8D, "CDOL2"}, + +{0x9F07, "AUC"}, // Application Usage Control +{0x9F6C, "CTQ"}, +{0x8E, "CVMList"}, +{0x9F0D, "IACDefault"}, +{0x9F0E, "IACDeny"}, +{0x9F0F, "IACOnline"}, + +{0x8F, "CertificationAuthorityPublicKeyIndex"}, +{0x9F32, "IssuerPublicKeyExponent"}, +{0x92, "IssuerPublicKeyRemainder"}, +{0x90, "IssuerPublicKeyCertificate"}, +{0x9F47, "ICCPublicKeyExponent"}, +{0x9F46, "ICCPublicKeyCertificate"}, + +{0x00, "end..."} +}; +int ApplicationDataLen = sizeof(ApplicationData) / sizeof(ApplicationDataElm); + +char* GetApplicationDataName(tlv_tag_t tag) { + for (int i = 0; i < ApplicationDataLen; i++) + if (ApplicationData[i].Tag == tag) + return ApplicationData[i].Name; + + return NULL; +} + +int JsonSaveStr(json_t *root, char *path, char *value) { + json_error_t error; + + if (strlen(path) < 1) + return 1; + + if (path[0] == '$') { + if (json_path_set(root, path, json_string(value), 0, &error)) { + PrintAndLog("ERROR: can't set json path: ", error.text); + return 2; + } else { + return 0; + } + } else { + return json_object_set_new(root, path, json_string(value)); + } +}; + +int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen) { + char * msg = sprint_hex(data, datalen); + if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ') + msg[strlen(msg) - 1] = '\0'; + + return JsonSaveStr(elm, path, msg); +} + +int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen) { + uint8_t bdata[8] = {0}; + int len = 0; + if (!datalen) { + for (uint64_t u = 0xffffffffffffffff; u; u = u << 8) { + if (!(data & u)) { + break; + } + len++; + } + if (!len) + len = 1; + } else { + len = datalen; + } + num_to_bytes(data, len, bdata); + + return JsonSaveBufAsHex(elm, path, bdata, len); +} + +int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm) { + const struct tlv *tlvelm = tlvdb_get_tlv(tlvdbelm); + if (tlvelm) + return JsonSaveBufAsHex(root, path, (uint8_t *)tlvelm->value, tlvelm->len); + else + return 1; +} + +int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink) { + json_error_t error; + + if (strlen(path) < 1 || !tlvelm) + return 1; + + if (path[0] == '$') { + + json_t *obj = json_path_get(elm, path); + if (!obj) { + obj = json_object(); + + if (json_is_array(elm)) { + if (json_array_append_new(elm, obj)) { + PrintAndLog("ERROR: can't append array: %s", path); + return 2; + } + } else { + if (json_path_set(elm, path, obj, 0, &error)) { + PrintAndLog("ERROR: can't set json path: ", error.text); + return 2; + } + } + } + + if (saveAppDataLink) { + char * AppDataName = GetApplicationDataName(tlvelm->tag); + if (AppDataName) + JsonSaveStr(obj, "appdata", AppDataName); + } else { + char * name = emv_get_tag_name(tlvelm); + if (saveName && name && strlen(name) > 0 && strncmp(name, "Unknown", 7)) + JsonSaveStr(obj, "name", emv_get_tag_name(tlvelm)); + JsonSaveHex(obj, "tag", tlvelm->tag, 0); + if (saveValue) { + JsonSaveHex(obj, "length", tlvelm->len, 0); + JsonSaveBufAsHex(obj, "value", (uint8_t *)tlvelm->value, tlvelm->len); + }; + } + } + + return 0; +} + +int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink) { + return JsonSaveTLVElm(elm, path, (struct tlv *)tlvdb_get_tlv(tlvdbelm), saveName, saveValue, saveAppDataLink); +} + +int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm) { + struct tlvdb *tlvp = tlvdbelm; + while (tlvp) { + const struct tlv * tlvpelm = tlvdb_get_tlv(tlvp); + char * AppDataName = NULL; + if (tlvpelm) + AppDataName = GetApplicationDataName(tlvpelm->tag); + + if (AppDataName) { + char appdatalink[200] = {0}; + sprintf(appdatalink, "$.ApplicationData.%s", AppDataName); + JsonSaveBufAsHex(root, appdatalink, (uint8_t *)tlvpelm->value, tlvpelm->len); + } + + json_t *pelm = json_path_get(elm, path); + if (pelm && json_is_array(pelm)) { + json_t *appendelm = json_object(); + json_array_append_new(pelm, appendelm); + JsonSaveTLVTreeElm(appendelm, "$", tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); + pelm = appendelm; + } else { + JsonSaveTLVTreeElm(elm, path, tlvp, !AppDataName, !tlvdb_elm_get_children(tlvp), AppDataName); + pelm = json_path_get(elm, path); + } + + if (tlvdb_elm_get_children(tlvp)) { + // get path element + if(!pelm) + return 1; + + // check childs element and add it if not found + json_t *chjson = json_path_get(pelm, "$.Childs"); + if (!chjson) { + json_object_set_new(pelm, "Childs", json_array()); + + chjson = json_path_get(pelm, "$.Childs"); + } + + // check + if (!json_is_array(chjson)) { + PrintAndLog("E->Internal logic error. `$.Childs` is not an array."); + break; + } + + // Recursion + JsonSaveTLVTree(root, chjson, "$", tlvdb_elm_get_children(tlvp)); + } + + tlvp = tlvdb_elm_get_next(tlvp); + } + return 0; +} + +bool HexToBuffer(const char *errormsg, const char *hexvalue, uint8_t * buffer, size_t maxbufferlen, size_t *bufferlen) { + int buflen = 0; + + switch(param_gethex_to_eol(hexvalue, 0, buffer, maxbufferlen, &buflen)) { + case 1: + PrintAndLog("%s Invalid HEX value.", errormsg); + return false; + case 2: + PrintAndLog("%s Hex value too large.", errormsg); + return false; + case 3: + PrintAndLog("%s Hex value must have even number of digits.", errormsg); + return false; + } + + if (buflen > maxbufferlen) { + PrintAndLog("%s HEX length (%d) more than %d", errormsg, *bufferlen, maxbufferlen); + return false; + } + + *bufferlen = buflen; + + return true; +} + +bool ParamLoadFromJson(struct tlvdb *tlv) { + json_t *root; + json_error_t error; + + if (!tlv) { + PrintAndLog("ERROR load params: tlv tree is NULL."); + return false; + } + + // current path + file name + const char *relfname = "emv/defparams.json"; + char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; + strcpy(fname, get_my_executable_directory()); + strcat(fname, relfname); + + root = json_load_file(fname, 0, &error); + if (!root) { + PrintAndLog("Load params: json error on line %d: %s", error.line, error.text); + return false; + } + + if (!json_is_array(root)) { + PrintAndLog("Load params: Invalid json format. root must be array."); + return false; + } + + PrintAndLog("Load params: json(%d) OK", json_array_size(root)); + + for(int i = 0; i < json_array_size(root); i++) { + json_t *data, *jtag, *jlength, *jvalue; + + data = json_array_get(root, i); + if(!json_is_object(data)) + { + PrintAndLog("Load params: data [%d] is not an object", i + 1); + json_decref(root); + return false; + } + + jtag = json_object_get(data, "tag"); + if(!json_is_string(jtag)) + { + PrintAndLog("Load params: data [%d] tag is not a string", i + 1); + json_decref(root); + return false; + } + const char *tlvTag = json_string_value(jtag); + + jvalue = json_object_get(data, "value"); + if(!json_is_string(jvalue)) + { + PrintAndLog("Load params: data [%d] value is not a string", i + 1); + json_decref(root); + return false; + } + const char *tlvValue = json_string_value(jvalue); + + jlength = json_object_get(data, "length"); + if(!json_is_number(jlength)) + { + PrintAndLog("Load params: data [%d] length is not a number", i + 1); + json_decref(root); + return false; + } + + int tlvLength = json_integer_value(jlength); + if (tlvLength > 250) { + PrintAndLog("Load params: data [%d] length more than 250", i + 1); + json_decref(root); + return false; + } + + PrintAndLog("TLV param: %s[%d]=%s", tlvTag, tlvLength, tlvValue); + uint8_t buf[251] = {0}; + size_t buflen = 0; + + // here max length must be 4, but now tlv_tag_t is 2-byte var. so let it be 2 by now... TODO: needs refactoring tlv_tag_t... + if (!HexToBuffer("TLV Error type:", tlvTag, buf, 2, &buflen)) { + json_decref(root); + return false; + } + tlv_tag_t tag = 0; + for (int i = 0; i < buflen; i++) { + tag = (tag << 8) + buf[i]; + } + + if (!HexToBuffer("TLV Error value:", tlvValue, buf, sizeof(buf) - 1, &buflen)) { + json_decref(root); + return false; + } + + if (buflen != tlvLength) { + PrintAndLog("Load params: data [%d] length of HEX must(%d) be identical to length in TLV param(%d)", i + 1, buflen, tlvLength); + json_decref(root); + return false; + } + + tlvdb_change_or_add_node(tlv, tag, tlvLength, (const unsigned char *)buf); + } + + json_decref(root); + + return true; +} + diff --git a/client/emv/emvjson.h b/client/emv/emvjson.h new file mode 100644 index 00000000..a518d7b9 --- /dev/null +++ b/client/emv/emvjson.h @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// EMV json logic +//----------------------------------------------------------------------------- +#ifndef EMVJSON_H__ +#define EMVJSON_H__ + +#include +#include "tlv.h" + +typedef struct { + tlv_tag_t Tag; + char *Name; +} ApplicationDataElm; + +extern char* GetApplicationDataName(tlv_tag_t tag); + +extern int JsonSaveStr(json_t *root, char *path, char *value); +extern int JsonSaveBufAsHex(json_t *elm, char *path, uint8_t *data, size_t datalen); +extern int JsonSaveHex(json_t *elm, char *path, uint64_t data, int datalen); + +extern int JsonSaveTLVValue(json_t *root, char *path, struct tlvdb *tlvdbelm); +extern int JsonSaveTLVElm(json_t *elm, char *path, struct tlv *tlvelm, bool saveName, bool saveValue, bool saveAppDataLink); +extern int JsonSaveTLVTreeElm(json_t *elm, char *path, struct tlvdb *tlvdbelm, bool saveName, bool saveValue, bool saveAppDataLink); + +extern int JsonSaveTLVTree(json_t *root, json_t *elm, char *path, struct tlvdb *tlvdbelm); + +extern bool ParamLoadFromJson(struct tlvdb *tlv); + +#endif \ No newline at end of file diff --git a/client/emv/test/cda_test.c b/client/emv/test/cda_test.c new file mode 100644 index 00000000..536a5862 --- /dev/null +++ b/client/emv/test/cda_test.c @@ -0,0 +1,442 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "../emv_pk.h" +#include "../crypto.h" +#include "../dump.h" +#include "../tlv.h" +#include "../emv_pki.h" + +#include +#include +#include + +struct emv_pk c_mchip_05 = { + .rid = { 0xa0, 0x00, 0x00, 0x00, 0x04, }, + .index = 5, + .hash_algo = HASH_SHA_1, + .pk_algo = PK_RSA, + .hash = { + 0xeb, 0xfa, 0x0d, 0x5d, + 0x06, 0xd8, 0xce, 0x70, + 0x2d, 0xa3, 0xea, 0xe8, + 0x90, 0x70, 0x1d, 0x45, + 0xe2, 0x74, 0xc8, 0x45, }, + .exp = { 0x03, }, + .elen = 1, + .mlen = 1408 / 8, + .modulus = (unsigned char[]){ + 0xb8, 0x04, 0x8a, 0xbc, 0x30, 0xc9, 0x0d, 0x97, 0x63, 0x36, 0x54, 0x3e, 0x3f, 0xd7, 0x09, 0x1c, + 0x8f, 0xe4, 0x80, 0x0d, 0xf8, 0x20, 0xed, 0x55, 0xe7, 0xe9, 0x48, 0x13, 0xed, 0x00, 0x55, 0x5b, + 0x57, 0x3f, 0xec, 0xa3, 0xd8, 0x4a, 0xf6, 0x13, 0x1a, 0x65, 0x1d, 0x66, 0xcf, 0xf4, 0x28, 0x4f, + 0xb1, 0x3b, 0x63, 0x5e, 0xdd, 0x0e, 0xe4, 0x01, 0x76, 0xd8, 0xbf, 0x04, 0xb7, 0xfd, 0x1c, 0x7b, + 0xac, 0xf9, 0xac, 0x73, 0x27, 0xdf, 0xaa, 0x8a, 0xa7, 0x2d, 0x10, 0xdb, 0x3b, 0x8e, 0x70, 0xb2, + 0xdd, 0xd8, 0x11, 0xcb, 0x41, 0x96, 0x52, 0x5e, 0xa3, 0x86, 0xac, 0xc3, 0x3c, 0x0d, 0x9d, 0x45, + 0x75, 0x91, 0x64, 0x69, 0xc4, 0xe4, 0xf5, 0x3e, 0x8e, 0x1c, 0x91, 0x2c, 0xc6, 0x18, 0xcb, 0x22, + 0xdd, 0xe7, 0xc3, 0x56, 0x8e, 0x90, 0x02, 0x2e, 0x6b, 0xba, 0x77, 0x02, 0x02, 0xe4, 0x52, 0x2a, + 0x2d, 0xd6, 0x23, 0xd1, 0x80, 0xe2, 0x15, 0xbd, 0x1d, 0x15, 0x07, 0xfe, 0x3d, 0xc9, 0x0c, 0xa3, + 0x10, 0xd2, 0x7b, 0x3e, 0xfc, 0xcd, 0x8f, 0x83, 0xde, 0x30, 0x52, 0xca, 0xd1, 0xe4, 0x89, 0x38, + 0xc6, 0x8d, 0x09, 0x5a, 0xac, 0x91, 0xb5, 0xf3, 0x7e, 0x28, 0xbb, 0x49, 0xec, 0x7e, 0xd5, 0x97, + }, +}; + +const unsigned char c_issuer_cert[] = { + 0x17, 0x14, 0x28, 0x4f, 0x76, 0x3b, 0x85, 0x86, 0xee, 0x6d, 0x31, 0x99, 0x51, 0xf7, 0xe6, 0x3f, + 0xa2, 0x50, 0x76, 0xe5, 0x0d, 0xc9, 0xd3, 0x20, 0x0b, 0xa9, 0x98, 0xd3, 0xa0, 0x52, 0xad, 0xba, + 0x9a, 0xb6, 0x9a, 0xc6, 0xad, 0x6a, 0xdd, 0x3c, 0xe0, 0x9f, 0x02, 0x78, 0xf4, 0x07, 0x4e, 0xc4, + 0xee, 0x9b, 0x1d, 0x22, 0x68, 0xa3, 0xe9, 0x53, 0x57, 0x5e, 0x45, 0x4e, 0x50, 0xcd, 0x86, 0x0b, + 0xf4, 0x24, 0xc5, 0x1c, 0x59, 0x77, 0x12, 0xd2, 0xaa, 0x05, 0x70, 0x89, 0xdd, 0x86, 0x73, 0xe5, + 0x1b, 0x1e, 0x1d, 0x71, 0x88, 0x03, 0x48, 0x92, 0x07, 0x7a, 0xc1, 0x8a, 0x6a, 0xe2, 0x34, 0x88, + 0xbe, 0xa9, 0xdf, 0x3b, 0x1a, 0x83, 0xf2, 0xc0, 0x80, 0x0c, 0xd7, 0xc5, 0xcd, 0xf2, 0xfd, 0xe0, + 0x49, 0x6f, 0x7b, 0xc3, 0x9f, 0xb4, 0xbf, 0x36, 0x32, 0x99, 0xbf, 0xa6, 0x37, 0xb2, 0xec, 0x33, + 0xc5, 0x07, 0xe3, 0x68, 0x21, 0xee, 0xc2, 0x07, 0x5f, 0x0e, 0x42, 0x0d, 0x38, 0xa1, 0xc9, 0xf3, + 0x12, 0x72, 0x61, 0xba, 0x31, 0x6c, 0x98, 0x76, 0x74, 0xfa, 0xdb, 0x20, 0xea, 0x7f, 0xeb, 0x75, + 0xee, 0x45, 0x5d, 0x12, 0x14, 0x6e, 0xa6, 0xf0, 0x2e, 0x8b, 0x01, 0xec, 0x2f, 0xa7, 0xa1, 0x15, +}; + +const unsigned char c_issuer_rem[] = { + 0x6e, 0x63, 0xb7, 0xbc, 0x70, 0xab, 0xdd, 0x09, 0x34, 0x1b, 0x34, 0xc0, 0x32, 0x86, 0xba, 0x9b, + 0xd8, 0x3b, 0xa7, 0x93, 0x6c, 0x5b, 0x77, 0x98, 0xfb, 0x22, 0xc5, 0xe5, 0x3f, 0xf2, 0x40, 0xa2, + 0x6d, 0xbd, 0x64, 0x15, +}; + +const unsigned char c_issuer_exp[] = { + 0x03, +}; + +const unsigned char c_icc_cert[] = { + 0xa4, 0x2f, 0xbe, 0xb1, 0x56, 0xb9, 0x8d, 0xcb, 0x05, 0x54, 0xda, 0x06, 0x2a, 0xdc, 0xa5, 0x30, + 0x9a, 0x91, 0xf0, 0x4f, 0xa2, 0xc7, 0xbd, 0x71, 0x02, 0xa8, 0xd7, 0x3f, 0x16, 0xa3, 0xcf, 0xad, + 0xe8, 0xaa, 0xdf, 0x4f, 0x3f, 0xe2, 0xa2, 0x12, 0x5c, 0xcd, 0xd7, 0x7c, 0x6b, 0x9f, 0x78, 0xb5, + 0xb4, 0x37, 0x1c, 0xe0, 0x80, 0x57, 0x25, 0xb0, 0xf9, 0xc0, 0x27, 0xaf, 0x14, 0x7d, 0x91, 0xe1, + 0xff, 0xdb, 0x20, 0x1e, 0x9c, 0x17, 0x0c, 0xe7, 0x77, 0x05, 0x3a, 0x17, 0x2a, 0xd5, 0x26, 0xdc, + 0xaf, 0xd3, 0x38, 0x95, 0xe1, 0xa9, 0x47, 0x30, 0x5c, 0x5b, 0x16, 0x7f, 0x2e, 0x7c, 0x6f, 0x99, + 0x15, 0x81, 0xa6, 0x52, 0xee, 0x47, 0x31, 0x54, 0x76, 0x0c, 0x2e, 0xd7, 0x74, 0x21, 0x4e, 0x50, + 0xdf, 0xec, 0xdd, 0x4c, 0xf2, 0x94, 0xc9, 0x74, 0xb8, 0x9e, 0xbc, 0xa2, 0x5b, 0x5a, 0xb3, 0xc0, + 0xbe, 0xb5, 0x0d, 0xfa, 0xf7, 0x82, 0xaf, 0xde, 0x14, 0x33, 0xd9, 0x0c, 0xa2, 0xa8, 0x9d, 0x65, + 0x1e, 0x75, 0xd6, 0x7e, 0xbc, 0x7c, 0x3e, 0x36, 0xf5, 0xa1, 0x65, 0xee, 0x61, 0x32, 0x61, 0x29, + 0x39, 0xc1, 0xec, 0xd3, 0x99, 0xe4, 0x60, 0x74, 0xb9, 0x96, 0xd9, 0x3a, 0x88, 0xe0, 0x1e, 0x0a, +}; + +const unsigned char c_icc_exp[] = { + 0x03, +}; + +const unsigned char c_sdad_cr[] = { + 0x1c, 0x00, 0x9f, 0xc4, 0x86, 0x79, 0x15, 0x7d, 0xbf, 0xf4, 0x5f, 0x65, 0xd3, 0x3f, 0xf7, 0x8d, + 0x4f, 0xcb, 0xf0, 0xcf, 0x5e, 0xa4, 0x20, 0x8d, 0x10, 0x7a, 0xe9, 0x5a, 0xa3, 0x8c, 0x54, 0x6d, + 0x0e, 0x5a, 0x18, 0xb8, 0x74, 0x03, 0xa1, 0x2b, 0xd4, 0x47, 0xa8, 0xbb, 0xfc, 0x1e, 0x49, 0xce, + 0x0b, 0x2e, 0x25, 0x13, 0x89, 0x20, 0x57, 0x03, 0xc9, 0xbb, 0x1a, 0x88, 0xcc, 0x79, 0xf1, 0xdd, + 0xc2, 0xf9, 0x84, 0x1e, 0xad, 0xf0, 0x7c, 0xe0, 0x7b, 0x62, 0x51, 0x1d, 0xdc, 0x93, 0xdf, 0x59, + 0xf2, 0x8f, 0x0e, 0x91, 0xf9, 0x23, 0x32, 0xd2, 0x9c, 0xde, 0xf2, 0xbc, 0xcb, 0x10, 0x08, 0x85, + 0x05, 0x00, 0xef, 0x3e, 0x47, 0x0a, 0x4c, 0xb1, 0x8c, 0xd9, 0x1a, 0xa5, 0xc1, 0xa1, 0x08, 0xf3, + +}; + +const unsigned char c_ssd1[] = { + 0x5f, 0x25, 0x03, 0x14, 0x05, 0x01, 0x5f, 0x24, 0x03, 0x15, 0x06, 0x30, 0x5a, 0x08, 0x52, 0x85, + 0x88, 0x12, 0x54, 0x34, 0x56, 0x53, 0x5f, 0x34, 0x01, 0x01, 0x8e, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x1f, 0x03, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05, + 0xbc, 0x50, 0xbc, 0x00, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05, + 0xbc, 0x70, 0xbc, 0x98, 0x00, 0x9f, 0x4a, 0x01, 0x82, 0x5f, 0x28, 0x02, 0x06, 0x43, 0x8c, 0x21, + 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, + 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x9f, 0x35, 0x01, 0x9f, 0x45, 0x02, 0x9f, 0x4c, 0x08, 0x9f, 0x34, + 0x03, 0x8d, 0x0c, 0x91, 0x0a, 0x8a, 0x02, 0x95, 0x05, 0x9f, 0x37, 0x04, 0x9f, 0x4c, 0x08, + 0x39, 0x00, +}; +static const struct tlv ssd1_tlv = { + .len = sizeof(c_ssd1), + .value = c_ssd1, +}; + +const unsigned char c_pan[] = { + 0x52, 0x85, 0x88, 0x12, 0x54, 0x34, 0x56, 0x53, +}; + +const unsigned char c_dd1[] = { + 0x12, 0x34, 0x57, 0x79, +}; + +const unsigned char c_dd2[] = { + 0x9f, 0x27, 0x01, 0x40, 0x9f, 0x36, 0x02, 0x00, 0x10, 0x9f, 0x10, 0x12, 0x00, 0x10, 0x90, 0x40, + 0x01, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +}; + +const unsigned char c_crm1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x43, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x43, 0x14, 0x09, 0x25, 0x50, 0x12, 0x34, 0x57, 0x79, 0x23, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00, +}; +static const struct tlv crm1_tlv = { + .len = sizeof(c_crm1), + .value = c_crm1, +}; + +static int cda_test_raw(bool verbose) +{ + const struct emv_pk *pk = &c_mchip_05; + + struct crypto_pk *kcp = crypto_pk_open(PK_RSA, + pk->modulus, pk->mlen, + pk->exp, pk->elen); + if (!kcp) + return 1; + + unsigned char *ipk_data; + size_t ipk_data_len; + ipk_data = crypto_pk_encrypt(kcp, c_issuer_cert, sizeof(c_issuer_cert), &ipk_data_len); + crypto_pk_close(kcp); + + if (!ipk_data) + return 1; + + if (verbose) { + printf("issuer cert:\n"); + dump_buffer(ipk_data, ipk_data_len, stdout, 0); + } + + size_t ipk_pk_len = ipk_data[13]; + unsigned char *ipk_pk = malloc(ipk_pk_len); + memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36); + memcpy(ipk_pk + ipk_data_len - 36, c_issuer_rem, sizeof(c_issuer_rem)); + + struct crypto_hash *ch; + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_write(ch, ipk_data + 1, 14); + crypto_hash_write(ch, ipk_pk, ipk_pk_len); + crypto_hash_write(ch, c_issuer_exp, sizeof(c_issuer_exp)); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + if (verbose) { + printf("crypto hash:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_close(ch); + free(ipk_data); + + struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len, + c_issuer_exp, (int) sizeof(c_issuer_exp)); + free(ipk_pk); + if (!ikcp) + return 1; + + unsigned char *iccpk_data; + size_t iccpk_data_len; + iccpk_data = crypto_pk_encrypt(ikcp, c_icc_cert, sizeof(c_icc_cert), &iccpk_data_len); + crypto_pk_close(ikcp); + + if (!iccpk_data) + return 1; + + if (verbose) { + printf("icc cert:\n"); + dump_buffer(iccpk_data, iccpk_data_len, stdout, 0); + } + + size_t iccpk_pk_len = iccpk_data[19]; + unsigned char *iccpk_pk = malloc(iccpk_pk_len); + 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));*/ + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + crypto_hash_write(ch, iccpk_data + 1, iccpk_data_len - 22); + crypto_hash_write(ch, c_icc_exp, sizeof(c_icc_exp)); + crypto_hash_write(ch, c_ssd1, sizeof(c_ssd1)); + + h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + if (verbose) { + printf("crypto hash1.1:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(iccpk_data + iccpk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + crypto_hash_close(ch); + free(iccpk_data); + + struct crypto_pk *icckcp = crypto_pk_open(PK_RSA, iccpk_pk, (int) iccpk_pk_len, + c_issuer_exp, (int) sizeof(c_issuer_exp)); + free(iccpk_pk); + if (!icckcp) + return 1; + + size_t sdad_len; + unsigned char *sdad = crypto_pk_encrypt(icckcp, c_sdad_cr, sizeof(c_sdad_cr), &sdad_len); + crypto_pk_close(icckcp); + if (!sdad) + return 1; + + if (verbose) { + printf("SDAD:\n"); + dump_buffer(sdad, sdad_len, stdout, 0); + } + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(sdad); + return 1; + } + + crypto_hash_write(ch, sdad + 1, sdad_len - 22); + crypto_hash_write(ch, c_dd1, sizeof(c_dd1)); + + unsigned char *h2 = crypto_hash_read(ch); + if (!h2) { + crypto_hash_close(ch); + free(sdad); + return 1; + } + + if (verbose) { + printf("crypto hash2:\n"); + dump_buffer(h2, 20, stdout, 0); + } + + crypto_hash_close(ch); + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(sdad); + return 1; + } + + crypto_hash_write(ch, c_crm1, sizeof(c_crm1)); + crypto_hash_write(ch, c_dd2, sizeof(c_dd2)); + + h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(sdad); + return 1; + } + + if (verbose) { + printf("crypto hash2.1:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(sdad + 5 + 8 + 1 + 8, h, 20)) { + crypto_hash_close(ch); + free(sdad); + return 1; + } + + crypto_hash_close(ch); + + free(sdad); + + return 0; +} + +static int cda_test_pk(bool verbose) +{ + const struct emv_pk *pk = &c_mchip_05; + struct tlvdb *db; + + db = tlvdb_external(0x90, sizeof(c_issuer_cert), c_issuer_cert); + tlvdb_add(db, tlvdb_external(0x9f32, sizeof(c_issuer_exp), c_issuer_exp)); + tlvdb_add(db, tlvdb_external(0x92, sizeof(c_issuer_rem), c_issuer_rem)); + tlvdb_add(db, tlvdb_external(0x5a, sizeof(c_pan), c_pan)); + + struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db); + if (!ipk) { + fprintf(stderr, "Could not recover Issuer certificate!\n"); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_external(0x9f46, sizeof(c_icc_cert), c_icc_cert)); + tlvdb_add(db, tlvdb_external(0x9f47, sizeof(c_icc_exp), c_icc_exp)); + /*tlvdb_add(db, tlvdb_external(0x9f48, sizeof(issuer_rem), issuer_rem));*/ + + struct emv_pk *iccpk = emv_pki_recover_icc_cert(ipk, db, &ssd1_tlv); + if (!iccpk) { + fprintf(stderr, "Could not recover ICC certificate!\n"); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_fixed(0x9f37, sizeof(c_dd1), c_dd1)); + + struct tlvdb *cda_db; + cda_db = tlvdb_fixed(0x9f27, 1, (unsigned char[]){ 0x40 }); + tlvdb_add(cda_db, tlvdb_fixed(0x9f36, 2, (unsigned char[]) { 0x00, 0x10 })); + tlvdb_add(cda_db, tlvdb_external(0x9f4b, sizeof(c_sdad_cr), c_sdad_cr)); + tlvdb_add(cda_db, tlvdb_fixed(0x9f10, 0x12, + (unsigned char[]) { 0x00, 0x10, 0x90, 0x40, 0x01, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff})); + + struct tlvdb *idndb = emv_pki_perform_cda(iccpk, db, cda_db, + NULL, + &crm1_tlv, + NULL); + if (!idndb) { + fprintf(stderr, "Could not recover IDN!\n"); + tlvdb_free(cda_db); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + const struct tlv *idn = tlvdb_get(idndb, 0x9f4c, NULL); + if (!idn) { + fprintf(stderr, "IDN not found!\n"); + tlvdb_free(idndb); + tlvdb_free(cda_db); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + if (verbose) { + printf("IDN:\n"); + dump_buffer(idn->value, idn->len, stdout, 0); + } + + tlvdb_free(idndb); + tlvdb_free(cda_db); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + + return 0; +} + +int exec_cda_test(bool verbose) +{ + int ret; + fprintf(stdout, "\n"); + + ret = cda_test_raw(verbose); + if (ret) { + fprintf(stderr, "CDA raw test: failed\n"); + return ret; + } + fprintf(stdout, "CDA raw test: passed\n"); + + ret = cda_test_pk(verbose); + if (ret) { + fprintf(stderr, "CDA test pk: failed\n"); + return ret; + } + fprintf(stdout, "CDA test pk: passed\n"); + + return 0; +} diff --git a/client/emv/test/cda_test.h b/client/emv/test/cda_test.h new file mode 100644 index 00000000..1d321a26 --- /dev/null +++ b/client/emv/test/cda_test.h @@ -0,0 +1,18 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#include + +extern int exec_cda_test(bool verbose); diff --git a/client/emv/test/crypto_test.c b/client/emv/test/crypto_test.c new file mode 100644 index 00000000..352f48b4 --- /dev/null +++ b/client/emv/test/crypto_test.c @@ -0,0 +1,327 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "../crypto.h" +#include "../dump.h" +#include "util_posix.h" + +#include +#include +#include + +static int test_genkey(unsigned int keylength, unsigned char *msg, size_t msg_len, bool verbose) +{ + int ret = 1; + size_t tmp_len, tmp2_len; + unsigned char *tmp, *tmp2; + struct crypto_pk *pk; + + printf("Testing key length %u ", keylength); + uint64_t ms = msclock(); + + pk = crypto_pk_genkey(PK_RSA, 1, keylength, 3); + if (!pk) { + fprintf(stderr, "ERROR: key generation error.\n"); + goto out; + } + + tmp_len = crypto_pk_get_nbits(pk); + if (tmp_len != keylength) { + fprintf(stderr, "ERROR: crypto_pk_get_nbits.\n"); + goto close; + } + + tmp = crypto_pk_decrypt(pk, msg, msg_len, &tmp_len); + if (!tmp) { + fprintf(stderr, "ERROR: crypto_pk_decrypt.\n"); + goto close; + } + + tmp2 = crypto_pk_encrypt(pk, tmp, tmp_len, &tmp2_len); + if (!tmp2) { + fprintf(stderr, "ERROR: crypto_pk_encrypt.\n"); + goto free_tmp; + } + + if (tmp2_len == msg_len && !memcmp(tmp2, msg, tmp2_len)) { + ret = 0; + } else { + fprintf(stderr, "ERROR: encrypt-decrypt sequence length or data error.\n"); + } + + free(tmp2); + printf("passed. (%"PRIu64" ms) \n", msclock() - ms); +free_tmp: + free(tmp); +close: + crypto_pk_close(pk); + +out: + return ret; +} + +static unsigned char message[4096 / 8] = + "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb" + "ccccccccccccccccdddddddddddddddd" + "eeeeeeeeeeeeeeeeffffffffffffffff" + "gggggggggggggggghhhhhhhhhhhhhhhh" + "iiiiiiiiiiiiiiiijjjjjjjjjjjjjjjj" + "kkkkkkkkkkkkkkkkllllllllllllllll" + "mmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnn" + "oooooooooooooooopppppppppppppppp" + "qqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrr" + "sssssssssssssssstttttttttttttttt" + "uuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvv" + "wwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxx" + "yyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzz" + "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb" + "ccccccccccccccccdddddddddddddddd" + "eeeeeeeeeeeeeeeeffffffffffffffff" + ; + +static unsigned char pk_N[] = { + 0xdb, 0x12, 0xe4, 0xf1, 0x8d, 0x43, 0x74, 0xf0, 0xec, 0x38, 0xdc, 0xfb, 0xf9, 0x20, 0x75, 0x6d, + 0x05, 0xf4, 0x36, 0xc2, 0x21, 0xac, 0x34, 0x0d, 0x16, 0xc5, 0x23, 0xcb, 0xfc, 0x9a, 0x8a, 0xd1, + 0xe0, 0xbd, 0xda, 0xe5, 0x77, 0xd5, 0xaf, 0x65, 0x8d, 0x6b, 0x62, 0x5c, 0xcd, 0x89, 0x06, 0x8d, + 0x11, 0x19, 0x6b, 0x0e, 0x3e, 0xe2, 0x80, 0x45, 0xf6, 0x44, 0x55, 0x21, 0x9c, 0x86, 0x90, 0x00, + 0xa8, 0xaf, 0x8c, 0x94, 0xde, 0x3f, 0xe8, 0x56, 0x52, 0xfe, 0xee, 0xa5, 0x36, 0x72, 0x07, 0xf2, + 0xcf, 0x8e, 0xf0, 0xbd, 0xff, 0x36, 0xd5, 0xf2, 0xad, 0x74, 0x1d, 0x17, 0xd0, 0xb7, 0x93, 0xe2, + 0x2c, 0x8d, 0x3f, 0xb6, 0x7c, 0x65, 0x19, 0x9f, 0xa7, 0x80, 0x1f, 0x9f, 0xe5, 0x2f, 0x2d, 0x75, + 0xc9, 0xc2, 0xe9, 0x70, 0xfa, 0x1e, 0x5a, 0xc6, 0xa3, 0x82, 0xd1, 0x29, 0x5a, 0x60, 0xce, 0x1f, + 0x40, 0x2e, 0xfc, 0x2a, 0x5e, 0xde, 0xc9, 0x67, 0xfc, 0x45, 0x18, 0xce, 0xf2, 0x83, 0x94, 0x53, + 0xd6, 0x4f, 0x2e, 0xc5, 0x2d, 0xa1, 0xa5, 0x7a, 0x63, 0x26, 0x70, 0xcb, 0x76, 0xfc, 0xb5, 0x8d, + 0x0f, 0x88, 0x4c, 0x07, 0xba, 0xfa, 0x8b, 0xbc, 0xa0, 0xea, 0xea, 0x0a, 0xe6, 0xa5, 0x44, 0xa5, + 0x0d, 0x12, 0x66, 0x2b, 0xf7, 0xc4, 0x76, 0xa3, 0x82, 0xa6, 0x2b, 0xb2, 0x5a, 0x27, 0xcd, 0x11, + 0xd2, 0x9d, 0x42, 0x86, 0x8c, 0x82, 0xc8, 0xe1, 0xff, 0x7d, 0xf1, 0xd9, 0xd9, 0xa1, 0xf3, 0x3d, + 0xc3, 0x12, 0x4e, 0x47, 0xc8, 0xa2, 0xcd, 0x72, 0x5a, 0x18, 0xea, 0x89, 0x5c, 0x73, 0x28, 0x52, + 0xf8, 0xdb, 0x70, 0xdc, 0x92, 0xc9, 0xb7, 0x98, 0x10, 0x94, 0x79, 0xdc, 0x9e, 0x12, 0x6c, 0x14, + 0x78, 0xf9, 0x5a, 0xad, 0x00, 0x98, 0xc8, 0x17, 0x79, 0x8a, 0xed, 0xe7, 0xc3, 0xd3, 0xa7, 0x8b, +}; + +static unsigned char pk_E[] = { + 0x01, 0x00, 0x01, +}; + +static unsigned char pk_D[] = { + 0x01, 0x17, 0xd4, 0x0a, 0x9c, 0x80, 0xd4, 0xa9, 0x8b, 0x14, 0x31, 0x8e, 0x14, 0x4d, 0x24, 0x28, + 0xda, 0x19, 0xc0, 0xd8, 0x31, 0x20, 0xd1, 0xd5, 0xaa, 0xe2, 0x6a, 0xee, 0x4e, 0xa1, 0x5a, 0xc5, + 0xf7, 0x50, 0x1b, 0x32, 0x7f, 0xe9, 0x92, 0x09, 0x78, 0xae, 0x2b, 0x7c, 0x79, 0x0e, 0x10, 0xf9, + 0x4d, 0x37, 0x8a, 0x40, 0x34, 0xf2, 0x1e, 0x5f, 0xba, 0xfd, 0xd6, 0x4a, 0xe7, 0xa4, 0x08, 0x3d, + 0xe8, 0x99, 0x8f, 0xa3, 0x02, 0x84, 0xe1, 0x1c, 0xe5, 0x27, 0x1e, 0x7b, 0xb6, 0x8c, 0xd5, 0x1b, + 0x52, 0x0b, 0xcd, 0x89, 0xb5, 0x27, 0x49, 0xe3, 0xff, 0x17, 0x90, 0x39, 0x99, 0x32, 0x01, 0x4b, + 0xe4, 0x9b, 0x03, 0xd1, 0x5e, 0x47, 0x86, 0xdc, 0x34, 0x12, 0xc0, 0x95, 0xa4, 0xa8, 0x1a, 0x9a, + 0xf6, 0xd9, 0xc1, 0x1e, 0x6e, 0x31, 0x0e, 0x94, 0xe5, 0x25, 0xf6, 0xf3, 0x34, 0xdf, 0x3c, 0xc8, + 0x0a, 0xc5, 0x8c, 0x00, 0x5c, 0x59, 0x55, 0x06, 0xd1, 0x39, 0x84, 0x35, 0x96, 0x40, 0xe8, 0xb2, + 0xf7, 0x13, 0x83, 0x37, 0xe1, 0xe2, 0x79, 0x41, 0x90, 0x2a, 0xc3, 0x71, 0xc5, 0xcf, 0xf0, 0xaa, + 0x01, 0x2f, 0x48, 0x9c, 0x3f, 0x29, 0x7b, 0xb7, 0x5c, 0xef, 0x25, 0xde, 0x34, 0x23, 0x81, 0x7a, + 0x4c, 0x3a, 0x9b, 0xe4, 0xa7, 0x44, 0x73, 0xbf, 0xf7, 0x39, 0x43, 0xa4, 0x39, 0xa0, 0x1b, 0xf7, + 0x4f, 0x5f, 0x14, 0x49, 0x32, 0x0e, 0x66, 0xd0, 0x29, 0xb5, 0x80, 0xe0, 0xba, 0x3b, 0x88, 0x2b, + 0x14, 0xa4, 0x26, 0x00, 0x2f, 0x50, 0x20, 0x4e, 0xfa, 0xc2, 0x44, 0x72, 0x72, 0x6c, 0x2a, 0x77, + 0x85, 0x20, 0xe0, 0x1d, 0x95, 0x6a, 0x66, 0xe7, 0xb8, 0xca, 0x5b, 0xc9, 0xc3, 0xf3, 0x39, 0xef, + 0xd7, 0xd5, 0x45, 0xb6, 0x3e, 0x19, 0xea, 0x7c, 0x56, 0x20, 0x1b, 0x95, 0x86, 0x2e, 0xc7, 0x51, +}; + +static unsigned char pk_P[] = { + 0xf5, 0x93, 0x0f, 0x76, 0x00, 0xab, 0x37, 0x01, 0xb9, 0x52, 0xb6, 0x82, 0xf9, 0xf5, 0xae, 0x29, + 0x8f, 0xd5, 0x08, 0xbc, 0xf7, 0x9f, 0x84, 0xb6, 0x4c, 0x94, 0xb5, 0xfc, 0xfe, 0xe1, 0xcd, 0x6a, + 0xf4, 0x9c, 0xa7, 0x33, 0xdb, 0xd8, 0xc8, 0xc1, 0xc0, 0x8d, 0x65, 0xed, 0x29, 0x99, 0x6c, 0x5c, + 0xbe, 0x08, 0xac, 0x04, 0xe4, 0x3a, 0x18, 0xe2, 0x0f, 0x70, 0x26, 0x70, 0x9b, 0x71, 0xfc, 0x9f, + 0x22, 0xea, 0x90, 0x3b, 0xc2, 0xa5, 0x16, 0x7a, 0xcd, 0x04, 0x3e, 0xa6, 0x37, 0x49, 0xa7, 0xee, + 0xaa, 0xe4, 0x9d, 0xaa, 0x9b, 0xb0, 0xe2, 0x6a, 0x9d, 0x1e, 0xcd, 0x83, 0x4e, 0xd8, 0x59, 0x6d, + 0x03, 0xd5, 0x4c, 0x5e, 0xc5, 0x22, 0x10, 0xb7, 0xcc, 0x0c, 0x90, 0x76, 0x05, 0x21, 0xe7, 0x77, + 0x5c, 0x88, 0x5f, 0xd0, 0x5f, 0x9e, 0x2e, 0x49, 0x56, 0xf4, 0x2b, 0xa9, 0x99, 0x57, 0x74, 0x19, +}; + +static unsigned char pk_Q[] = { + 0xe4, 0x5f, 0xd2, 0x28, 0xbd, 0xf3, 0xdd, 0x70, 0x3d, 0xfd, 0x01, 0x23, 0xae, 0x93, 0x6a, 0x91, + 0xca, 0x68, 0xb1, 0xdb, 0x81, 0xab, 0x1e, 0x63, 0x76, 0x9b, 0x6d, 0xaa, 0x41, 0x87, 0x5a, 0x79, + 0xe7, 0xce, 0xd6, 0x84, 0x32, 0x53, 0xf5, 0xfc, 0xb7, 0x41, 0x7c, 0xcb, 0x88, 0x09, 0xcb, 0xe9, + 0x07, 0x16, 0x28, 0x55, 0x23, 0xe5, 0xf2, 0xf5, 0x23, 0xf5, 0xee, 0x2b, 0x9d, 0x91, 0x56, 0xc6, + 0x30, 0x91, 0x4d, 0x16, 0x11, 0x6c, 0x48, 0x45, 0xe8, 0x5d, 0x0e, 0x9e, 0x04, 0xc8, 0xb6, 0xdd, + 0xba, 0x0d, 0xdf, 0x53, 0x56, 0xfa, 0x0b, 0x0b, 0x99, 0x8d, 0xea, 0x5c, 0x45, 0x7d, 0xed, 0xad, + 0x1f, 0xc5, 0xc1, 0x7d, 0x63, 0x31, 0xf8, 0x32, 0xb5, 0x33, 0xb0, 0xef, 0xce, 0x2e, 0x74, 0x1e, + 0x77, 0x2a, 0x18, 0x35, 0x3d, 0x6e, 0x01, 0xba, 0xde, 0x21, 0x8e, 0x14, 0x12, 0xc3, 0x0d, 0x43, +}; + +static unsigned char pk_dP[] = { + 0x5a, 0xc8, 0xf7, 0x1a, 0x44, 0xbd, 0x07, 0x24, 0xd8, 0x02, 0x3f, 0xfe, 0xc3, 0xb1, 0x93, 0xa5, + 0x41, 0xcb, 0x1b, 0xe3, 0xe0, 0x17, 0x54, 0xd4, 0xa0, 0x13, 0x0a, 0x04, 0x71, 0xa5, 0xc0, 0x6f, + 0x1d, 0xe7, 0x1b, 0xd9, 0x0c, 0x19, 0x64, 0x7e, 0x5c, 0x54, 0xe9, 0xad, 0x77, 0x87, 0x84, 0x8b, + 0xf4, 0xa4, 0xf8, 0x13, 0x06, 0xdc, 0x83, 0x7e, 0x6e, 0xfe, 0xa2, 0xf7, 0x56, 0x40, 0x19, 0x88, + 0x2b, 0x3c, 0x53, 0xfe, 0x03, 0xc3, 0x4c, 0x40, 0x31, 0xb2, 0xb4, 0x06, 0x76, 0xc2, 0x00, 0x17, + 0x37, 0x8e, 0x34, 0xcb, 0x71, 0xab, 0x3e, 0xc8, 0xf3, 0x35, 0x03, 0xfc, 0xdb, 0x15, 0x18, 0x5a, + 0x38, 0xe4, 0x8d, 0xcb, 0x2b, 0x4d, 0xa0, 0xa8, 0x92, 0x02, 0xc3, 0x15, 0x1e, 0x68, 0x9e, 0x4d, + 0x7e, 0x23, 0xdc, 0x68, 0x08, 0x31, 0x4e, 0x23, 0x46, 0xc6, 0x15, 0xae, 0x29, 0x46, 0x2f, 0x61, +}; + +static unsigned char pk_dQ[] = { + 0x33, 0x61, 0x9f, 0xae, 0x0c, 0xf6, 0xc6, 0x16, 0x8f, 0xcb, 0xd1, 0xaa, 0xce, 0x87, 0x5a, 0x4d, + 0xcc, 0xe5, 0x7b, 0x46, 0xb0, 0xc8, 0xe8, 0x40, 0x66, 0x9a, 0x17, 0xb5, 0x5b, 0xa2, 0xf1, 0x67, + 0x46, 0x11, 0x52, 0x50, 0x51, 0xe6, 0x74, 0x0c, 0xd4, 0xca, 0x46, 0x22, 0xa0, 0xcb, 0xdb, 0x75, + 0xe5, 0x63, 0x45, 0xd5, 0xca, 0x0a, 0xdd, 0x7b, 0xec, 0x08, 0x53, 0xfa, 0xba, 0x2b, 0xce, 0x03, + 0x2f, 0x40, 0x31, 0xc0, 0xca, 0x50, 0xbb, 0x7e, 0x07, 0x06, 0x90, 0xd8, 0x5a, 0xa9, 0x32, 0x03, + 0x76, 0xed, 0xd2, 0x16, 0x35, 0x16, 0x72, 0xcf, 0xbc, 0x4f, 0xa2, 0xaf, 0xf9, 0xee, 0x98, 0x40, + 0x00, 0x4b, 0x04, 0xfa, 0x8a, 0x0b, 0xdf, 0x14, 0xc1, 0x92, 0x0c, 0xb8, 0x17, 0x82, 0x7a, 0x1b, + 0xb4, 0xa1, 0xe2, 0xea, 0x6f, 0x94, 0xc5, 0x8c, 0xde, 0x97, 0x5c, 0x19, 0x06, 0x13, 0x9e, 0x73, +}; + +static unsigned char pk_I[] = { + 0x75, 0x40, 0xc6, 0x02, 0x7e, 0x4f, 0xad, 0xdb, 0x95, 0xac, 0x07, 0x8d, 0x80, 0xb6, 0x80, 0x02, + 0x06, 0xdd, 0xb8, 0x5d, 0x92, 0x65, 0x69, 0x26, 0x86, 0x61, 0x6c, 0x87, 0x4e, 0xe5, 0x03, 0x68, + 0xc6, 0x10, 0x15, 0x8c, 0x43, 0x3a, 0x45, 0x63, 0x48, 0xb7, 0x8a, 0x8c, 0xa2, 0x2b, 0x34, 0xb6, + 0x83, 0xfe, 0xa8, 0x10, 0xa5, 0x74, 0xa5, 0xa9, 0x52, 0x42, 0x1f, 0xa0, 0x80, 0x6a, 0xc5, 0x35, + 0xe8, 0xb8, 0xc2, 0xa0, 0x3f, 0x49, 0x18, 0xcf, 0x0e, 0x54, 0x3c, 0x70, 0x11, 0x11, 0xd3, 0x85, + 0x8c, 0xb2, 0xe5, 0x74, 0xdf, 0x98, 0xea, 0x6c, 0xc0, 0x5f, 0x7f, 0xff, 0x69, 0xbf, 0x08, 0x8d, + 0x1b, 0xc4, 0x90, 0xcc, 0xa4, 0xcd, 0xcc, 0x34, 0x58, 0xe5, 0x91, 0x53, 0x3a, 0xd5, 0x39, 0xf4, + 0xd4, 0x42, 0xc9, 0x17, 0xb2, 0x2c, 0x92, 0x12, 0x37, 0x1b, 0xd3, 0xc5, 0x79, 0xd2, 0x65, 0x61, +}; + +static int test_pk(bool verbose) +{ + int ret = 1; + size_t tmp_len, tmp2_len; + unsigned char *tmp, *tmp2; + struct crypto_pk *pubk, *privk; + unsigned char *msg = message; + size_t msg_len = sizeof(pk_N); + + printf("Testing public keys interfaces\n"); + + pubk = crypto_pk_open(PK_RSA, + pk_N, sizeof(pk_N), + pk_E, sizeof(pk_E)); + if (!pubk) { + fprintf(stderr, "ERROR: open public key.\n"); + return 1; + } + + tmp_len = crypto_pk_get_nbits(pubk); + if (tmp_len != sizeof(pk_N) * 8) { + fprintf(stderr, "ERROR: crypto_pk_get_nbits mismatch.\n"); + goto close_pub; + } + + tmp = crypto_pk_get_parameter(pubk, 0, &tmp_len); + if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) { + fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %zd len %zd\n", tmp_len, sizeof(pk_N)); + free(tmp); + goto close_pub; + } + free(tmp); + + tmp = crypto_pk_get_parameter(pubk, 1, &tmp_len); + if (tmp_len != sizeof(pk_E) || memcmp(tmp, pk_E, tmp_len)) { + fprintf(stderr, "ERROR: crypto_pk_get_parameter(1) Exponent.\n"); + free(tmp); + goto close_pub; + } + free(tmp); + + privk = crypto_pk_open_priv(PK_RSA, + pk_N, sizeof(pk_N), + pk_E, sizeof(pk_E), + pk_D, sizeof(pk_D), + pk_P, sizeof(pk_P), + pk_Q, sizeof(pk_Q), + pk_dP, sizeof(pk_dP), + pk_dQ, sizeof(pk_dQ), + pk_I, sizeof(pk_I)); + if (!privk) { + fprintf(stderr, "ERROR: open private key.\n"); + goto close_pub; + } + + + tmp_len = crypto_pk_get_nbits(privk); + if (tmp_len != sizeof(pk_N) * 8) { + fprintf(stderr, "ERROR: crypto_pk_get_nbits mismatch.\n"); + goto close_pub; + } + + tmp = crypto_pk_get_parameter(privk, 0, &tmp_len); + if (tmp_len != sizeof(pk_N) || memcmp(tmp, pk_N, tmp_len)) { + fprintf(stderr, "ERROR: crypto_pk_get_parameter(0) Modulus. param len %zd len %zd\n", tmp_len, sizeof(pk_N)); + free(tmp); + goto close; + } + free(tmp); + + tmp = crypto_pk_get_parameter(privk, 1, &tmp_len); + if (tmp_len != sizeof(pk_E) || memcmp(tmp, pk_E, tmp_len)) { + fprintf(stderr, "ERROR: crypto_pk_get_parameter(1) Exponent.\n"); + free(tmp); + goto close; + } + free(tmp); + + tmp = crypto_pk_decrypt(privk, msg, msg_len, &tmp_len); + if (!tmp) { + fprintf(stderr, "ERROR: crypto_pk_decrypt.\n"); + goto close; + } + + tmp2 = crypto_pk_encrypt(pubk, tmp, tmp_len, &tmp2_len); + if (!tmp2) { + fprintf(stderr, "ERROR: crypto_pk_encrypt.\n"); + goto free_tmp; + } + + if (tmp2_len == msg_len && !memcmp(tmp2, msg, tmp2_len)) { + ret = 0; + } else { + fprintf(stderr, "ERROR: encrypt-decrypt sequence length or data error.\n"); + } + + free(tmp2); +free_tmp: + free(tmp); + +close: + crypto_pk_close(privk); +close_pub: + crypto_pk_close(pubk); + + return ret; +} + +int exec_crypto_test(bool verbose) +{ + unsigned int keylengths[] = {1024, 1152, 1408, 1984, 2048, 3072, 4096}; + int i; + int ret; + fprintf(stdout, "\n"); + + ret = test_pk(verbose); + if (ret) { + fprintf(stderr, "Crypto raw test: failed\n"); + return ret; + } + fprintf(stdout, "Crypto raw test: passed\n\n"); + + for (i = 0; i < sizeof(keylengths) / sizeof(keylengths[0]); i++) { + unsigned int kl = keylengths[i]; + ret = test_genkey(kl, message, kl / 8, verbose); + if (ret) { + fprintf(stderr, "Crypto generate key[%d] test: failed\n", kl); + return ret; + } + } + + return 0; +} diff --git a/client/emv/test/crypto_test.h b/client/emv/test/crypto_test.h new file mode 100644 index 00000000..084bc991 --- /dev/null +++ b/client/emv/test/crypto_test.h @@ -0,0 +1,18 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + + #include + +extern int exec_crypto_test(bool verbose); diff --git a/client/emv/test/cryptotest.c b/client/emv/test/cryptotest.c new file mode 100644 index 00000000..b778d256 --- /dev/null +++ b/client/emv/test/cryptotest.c @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Crypto algorithms testing +//----------------------------------------------------------------------------- + +#include "cryptotest.h" +#include "util.h" +#include "ui.h" + +#include "bignum.h" +#include "aes.h" +#include "aes_cmac128.h" +#include "des.h" +#include "rsa.h" +#include "sha1.h" + +#include "crypto_test.h" +#include "sda_test.h" +#include "dda_test.h" +#include "cda_test.h" + +int ExecuteCryptoTests(bool verbose) { + int res; + bool TestFail = false; + + res = mpi_self_test(verbose); + if (res) TestFail = true; + + res = aes_self_test(verbose); + if (res) TestFail = true; + + res = aes_cmac_self_test(verbose); + if (res) TestFail = true; + + res = des_self_test(verbose); + if (res) TestFail = true; + + res = sha1_self_test(verbose); + if (res) TestFail = true; + + res = rsa_self_test(verbose); + if (res) TestFail = true; + + res = exec_sda_test(verbose); + if (res) TestFail = true; + + res = exec_dda_test(verbose); + if (res) TestFail = true; + + res = exec_cda_test(verbose); + if (res) TestFail = true; + + res = exec_crypto_test(verbose); + if (res) TestFail = true; + + PrintAndLog("\n--------------------------"); + if (TestFail) + PrintAndLog("Test(s) [ERROR]."); + else + PrintAndLog("Tests [OK]."); + + return TestFail; +} + diff --git a/client/emv/test/cryptotest.h b/client/emv/test/cryptotest.h new file mode 100644 index 00000000..5c6aaae2 --- /dev/null +++ b/client/emv/test/cryptotest.h @@ -0,0 +1,13 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Crypto algorithms testing +//----------------------------------------------------------------------------- + +#include + +extern int ExecuteCryptoTests(bool verbose); diff --git a/client/emv/test/dda_test.c b/client/emv/test/dda_test.c new file mode 100644 index 00000000..afdd4e20 --- /dev/null +++ b/client/emv/test/dda_test.c @@ -0,0 +1,390 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "dda_test.h" + +#include "../emv_pk.h" +#include "../crypto.h" +#include "../dump.h" +#include "../tlv.h" +#include "../emv_pki.h" + +#include +#include +#include + +struct emv_pk mchip_05 = { + .rid = { 0xa0, 0x00, 0x00, 0x00, 0x04, }, + .index = 5, + .hash_algo = HASH_SHA_1, + .pk_algo = PK_RSA, + .hash = { + 0xeb, 0xfa, 0x0d, 0x5d, + 0x06, 0xd8, 0xce, 0x70, + 0x2d, 0xa3, 0xea, 0xe8, + 0x90, 0x70, 0x1d, 0x45, + 0xe2, 0x74, 0xc8, 0x45, }, + .exp = { 0x03, }, + .elen = 1, + .mlen = 1408 / 8, + .modulus = (unsigned char[]){ + 0xb8, 0x04, 0x8a, 0xbc, 0x30, 0xc9, 0x0d, 0x97, 0x63, 0x36, 0x54, 0x3e, 0x3f, 0xd7, 0x09, 0x1c, + 0x8f, 0xe4, 0x80, 0x0d, 0xf8, 0x20, 0xed, 0x55, 0xe7, 0xe9, 0x48, 0x13, 0xed, 0x00, 0x55, 0x5b, + 0x57, 0x3f, 0xec, 0xa3, 0xd8, 0x4a, 0xf6, 0x13, 0x1a, 0x65, 0x1d, 0x66, 0xcf, 0xf4, 0x28, 0x4f, + 0xb1, 0x3b, 0x63, 0x5e, 0xdd, 0x0e, 0xe4, 0x01, 0x76, 0xd8, 0xbf, 0x04, 0xb7, 0xfd, 0x1c, 0x7b, + 0xac, 0xf9, 0xac, 0x73, 0x27, 0xdf, 0xaa, 0x8a, 0xa7, 0x2d, 0x10, 0xdb, 0x3b, 0x8e, 0x70, 0xb2, + 0xdd, 0xd8, 0x11, 0xcb, 0x41, 0x96, 0x52, 0x5e, 0xa3, 0x86, 0xac, 0xc3, 0x3c, 0x0d, 0x9d, 0x45, + 0x75, 0x91, 0x64, 0x69, 0xc4, 0xe4, 0xf5, 0x3e, 0x8e, 0x1c, 0x91, 0x2c, 0xc6, 0x18, 0xcb, 0x22, + 0xdd, 0xe7, 0xc3, 0x56, 0x8e, 0x90, 0x02, 0x2e, 0x6b, 0xba, 0x77, 0x02, 0x02, 0xe4, 0x52, 0x2a, + 0x2d, 0xd6, 0x23, 0xd1, 0x80, 0xe2, 0x15, 0xbd, 0x1d, 0x15, 0x07, 0xfe, 0x3d, 0xc9, 0x0c, 0xa3, + 0x10, 0xd2, 0x7b, 0x3e, 0xfc, 0xcd, 0x8f, 0x83, 0xde, 0x30, 0x52, 0xca, 0xd1, 0xe4, 0x89, 0x38, + 0xc6, 0x8d, 0x09, 0x5a, 0xac, 0x91, 0xb5, 0xf3, 0x7e, 0x28, 0xbb, 0x49, 0xec, 0x7e, 0xd5, 0x97, + }, +}; + +const unsigned char d_issuer_cert[] = { + 0x17, 0x14, 0x28, 0x4f, 0x76, 0x3b, 0x85, 0x86, 0xee, 0x6d, 0x31, 0x99, 0x51, 0xf7, 0xe6, 0x3f, + 0xa2, 0x50, 0x76, 0xe5, 0x0d, 0xc9, 0xd3, 0x20, 0x0b, 0xa9, 0x98, 0xd3, 0xa0, 0x52, 0xad, 0xba, + 0x9a, 0xb6, 0x9a, 0xc6, 0xad, 0x6a, 0xdd, 0x3c, 0xe0, 0x9f, 0x02, 0x78, 0xf4, 0x07, 0x4e, 0xc4, + 0xee, 0x9b, 0x1d, 0x22, 0x68, 0xa3, 0xe9, 0x53, 0x57, 0x5e, 0x45, 0x4e, 0x50, 0xcd, 0x86, 0x0b, + 0xf4, 0x24, 0xc5, 0x1c, 0x59, 0x77, 0x12, 0xd2, 0xaa, 0x05, 0x70, 0x89, 0xdd, 0x86, 0x73, 0xe5, + 0x1b, 0x1e, 0x1d, 0x71, 0x88, 0x03, 0x48, 0x92, 0x07, 0x7a, 0xc1, 0x8a, 0x6a, 0xe2, 0x34, 0x88, + 0xbe, 0xa9, 0xdf, 0x3b, 0x1a, 0x83, 0xf2, 0xc0, 0x80, 0x0c, 0xd7, 0xc5, 0xcd, 0xf2, 0xfd, 0xe0, + 0x49, 0x6f, 0x7b, 0xc3, 0x9f, 0xb4, 0xbf, 0x36, 0x32, 0x99, 0xbf, 0xa6, 0x37, 0xb2, 0xec, 0x33, + 0xc5, 0x07, 0xe3, 0x68, 0x21, 0xee, 0xc2, 0x07, 0x5f, 0x0e, 0x42, 0x0d, 0x38, 0xa1, 0xc9, 0xf3, + 0x12, 0x72, 0x61, 0xba, 0x31, 0x6c, 0x98, 0x76, 0x74, 0xfa, 0xdb, 0x20, 0xea, 0x7f, 0xeb, 0x75, + 0xee, 0x45, 0x5d, 0x12, 0x14, 0x6e, 0xa6, 0xf0, 0x2e, 0x8b, 0x01, 0xec, 0x2f, 0xa7, 0xa1, 0x15, +}; + +const unsigned char d_issuer_rem[] = { + 0x6e, 0x63, 0xb7, 0xbc, 0x70, 0xab, 0xdd, 0x09, 0x34, 0x1b, 0x34, 0xc0, 0x32, 0x86, 0xba, 0x9b, + 0xd8, 0x3b, 0xa7, 0x93, 0x6c, 0x5b, 0x77, 0x98, 0xfb, 0x22, 0xc5, 0xe5, 0x3f, 0xf2, 0x40, 0xa2, + 0x6d, 0xbd, 0x64, 0x15, +}; + +const unsigned char d_issuer_exp[] = { + 0x03, +}; + +const unsigned char d_icc_cert[] = { + 0xa4, 0x2f, 0xbe, 0xb1, 0x56, 0xb9, 0x8d, 0xcb, 0x05, 0x54, 0xda, 0x06, 0x2a, 0xdc, 0xa5, 0x30, + 0x9a, 0x91, 0xf0, 0x4f, 0xa2, 0xc7, 0xbd, 0x71, 0x02, 0xa8, 0xd7, 0x3f, 0x16, 0xa3, 0xcf, 0xad, + 0xe8, 0xaa, 0xdf, 0x4f, 0x3f, 0xe2, 0xa2, 0x12, 0x5c, 0xcd, 0xd7, 0x7c, 0x6b, 0x9f, 0x78, 0xb5, + 0xb4, 0x37, 0x1c, 0xe0, 0x80, 0x57, 0x25, 0xb0, 0xf9, 0xc0, 0x27, 0xaf, 0x14, 0x7d, 0x91, 0xe1, + 0xff, 0xdb, 0x20, 0x1e, 0x9c, 0x17, 0x0c, 0xe7, 0x77, 0x05, 0x3a, 0x17, 0x2a, 0xd5, 0x26, 0xdc, + 0xaf, 0xd3, 0x38, 0x95, 0xe1, 0xa9, 0x47, 0x30, 0x5c, 0x5b, 0x16, 0x7f, 0x2e, 0x7c, 0x6f, 0x99, + 0x15, 0x81, 0xa6, 0x52, 0xee, 0x47, 0x31, 0x54, 0x76, 0x0c, 0x2e, 0xd7, 0x74, 0x21, 0x4e, 0x50, + 0xdf, 0xec, 0xdd, 0x4c, 0xf2, 0x94, 0xc9, 0x74, 0xb8, 0x9e, 0xbc, 0xa2, 0x5b, 0x5a, 0xb3, 0xc0, + 0xbe, 0xb5, 0x0d, 0xfa, 0xf7, 0x82, 0xaf, 0xde, 0x14, 0x33, 0xd9, 0x0c, 0xa2, 0xa8, 0x9d, 0x65, + 0x1e, 0x75, 0xd6, 0x7e, 0xbc, 0x7c, 0x3e, 0x36, 0xf5, 0xa1, 0x65, 0xee, 0x61, 0x32, 0x61, 0x29, + 0x39, 0xc1, 0xec, 0xd3, 0x99, 0xe4, 0x60, 0x74, 0xb9, 0x96, 0xd9, 0x3a, 0x88, 0xe0, 0x1e, 0x0a, +}; + +const unsigned char d_icc_exp[] = { + 0x03, +}; + +const unsigned char d_sdad_cr[] = { + 0x3d, 0x87, 0xf3, 0x10, 0x56, 0x10, 0x2d, 0x25, 0x12, 0xcf, 0xde, 0x30, 0x90, 0x06, 0x27, 0xc1, + 0x26, 0x3a, 0x76, 0xd1, 0xda, 0xa8, 0x21, 0xf5, 0x08, 0x31, 0xe6, 0x06, 0xc5, 0x45, 0x44, 0xc2, + 0x58, 0x13, 0x1e, 0xae, 0xbe, 0x87, 0x4d, 0xcb, 0x1a, 0x28, 0xcf, 0x82, 0xd3, 0xff, 0x91, 0x11, + 0x82, 0x60, 0xbc, 0x91, 0x11, 0x37, 0x11, 0xd3, 0xb2, 0x89, 0xfa, 0x41, 0xbe, 0x69, 0xc7, 0xa7, + 0xb5, 0xc7, 0x83, 0xe6, 0xf8, 0xf9, 0x7f, 0xce, 0x13, 0xf0, 0x8b, 0x13, 0xfa, 0x44, 0x18, 0x3e, + 0x37, 0x18, 0xce, 0xbf, 0x0c, 0x41, 0x47, 0x3d, 0x2b, 0x0f, 0xf4, 0xde, 0x44, 0xb6, 0xa0, 0x2d, + 0x75, 0xad, 0xb6, 0xd4, 0x96, 0x23, 0x93, 0xff, 0xdf, 0x4e, 0x69, 0x02, 0x6c, 0xdf, 0x38, 0xff, +}; + +const unsigned char d_ssd1[] = { + 0x5f, 0x25, 0x03, 0x14, 0x05, 0x01, 0x5f, 0x24, 0x03, 0x15, 0x06, 0x30, 0x5a, 0x08, 0x52, 0x85, + 0x88, 0x12, 0x54, 0x34, 0x56, 0x53, 0x5f, 0x34, 0x01, 0x01, 0x8e, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x1f, 0x03, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05, + 0xbc, 0x50, 0xbc, 0x00, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05, + 0xbc, 0x70, 0xbc, 0x98, 0x00, 0x9f, 0x4a, 0x01, 0x82, 0x5f, 0x28, 0x02, 0x06, 0x43, 0x8c, 0x21, + 0x9f, 0x02, 0x06, 0x9f, 0x03, 0x06, 0x9f, 0x1a, 0x02, 0x95, 0x05, 0x5f, 0x2a, 0x02, 0x9a, 0x03, + 0x9c, 0x01, 0x9f, 0x37, 0x04, 0x9f, 0x35, 0x01, 0x9f, 0x45, 0x02, 0x9f, 0x4c, 0x08, 0x9f, 0x34, + 0x03, 0x8d, 0x0c, 0x91, 0x0a, 0x8a, 0x02, 0x95, 0x05, 0x9f, 0x37, 0x04, 0x9f, 0x4c, 0x08, + 0x39, 0x00, +}; +static const struct tlv ssd1_tlv = { + .len = sizeof(d_ssd1), + .value = d_ssd1, +}; + +const unsigned char d_pan[] = { + 0x52, 0x85, 0x88, 0x12, 0x54, 0x34, 0x56, 0x53, +}; + +const unsigned char d_dd1[] = { + 0x00, 0x00, 0x00, 0x00, +}; +static const struct tlv dd1_tlv = { + .len = sizeof(d_dd1), + .value = d_dd1, +}; + +static int dda_test_raw(bool verbose) +{ + const struct emv_pk *pk = &mchip_05; + + struct crypto_pk *kcp = crypto_pk_open(PK_RSA, + pk->modulus, pk->mlen, + pk->exp, pk->elen); + if (!kcp) + return 1; + + unsigned char *ipk_data; + size_t ipk_data_len; + ipk_data = crypto_pk_encrypt(kcp, d_issuer_cert, sizeof(d_issuer_cert), &ipk_data_len); + crypto_pk_close(kcp); + + if (!ipk_data) + return 1; + + if (verbose) { + printf("issuer cert:\n"); + dump_buffer(ipk_data, ipk_data_len, stdout, 0); + } + + size_t ipk_pk_len = ipk_data[13]; + unsigned char *ipk_pk = malloc(ipk_pk_len); + memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36); + memcpy(ipk_pk + ipk_data_len - 36, d_issuer_rem, sizeof(d_issuer_rem)); + + struct crypto_hash *ch; + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_write(ch, ipk_data + 1, 14); + crypto_hash_write(ch, ipk_pk, ipk_pk_len); + crypto_hash_write(ch, d_issuer_exp, sizeof(d_issuer_exp)); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + if (verbose) { + printf("crypto hash:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_close(ch); + free(ipk_data); + + struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len, + d_issuer_exp, (int) sizeof(d_issuer_exp)); + free(ipk_pk); + if (!ikcp) + return 1; + + unsigned char *iccpk_data; + size_t iccpk_data_len; + iccpk_data = crypto_pk_encrypt(ikcp, d_icc_cert, sizeof(d_icc_cert), &iccpk_data_len); + crypto_pk_close(ikcp); + + if (!iccpk_data) + return 1; + + if (verbose) { + printf("icc cert:\n"); + dump_buffer(iccpk_data, iccpk_data_len, stdout, 0); + } + + size_t iccpk_pk_len = iccpk_data[19]; + unsigned char *iccpk_pk = malloc(iccpk_pk_len); + 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));*/ + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + crypto_hash_write(ch, iccpk_data + 1, iccpk_data_len - 22); + crypto_hash_write(ch, d_icc_exp, sizeof(d_icc_exp)); + crypto_hash_write(ch, d_ssd1, sizeof(d_ssd1)); + + h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + if (verbose) { + printf("crypto hash1.1:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(iccpk_data + iccpk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(iccpk_pk); + free(iccpk_data); + return 1; + } + + crypto_hash_close(ch); + free(iccpk_data); + + struct crypto_pk *icckcp = crypto_pk_open(PK_RSA, iccpk_pk, (int) iccpk_pk_len, + d_issuer_exp, (int) sizeof(d_issuer_exp)); + free(iccpk_pk); + if (!icckcp) + return 1; + + size_t sdad_len; + unsigned char *sdad = crypto_pk_encrypt(icckcp, d_sdad_cr, sizeof(d_sdad_cr), &sdad_len); + crypto_pk_close(icckcp); + if (!sdad) + return 1; + + if (verbose) { + printf("sdad:\n"); + dump_buffer(sdad, sdad_len, stdout, 0); + } + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(sdad); + return 1; + } + + crypto_hash_write(ch, sdad + 1, sdad_len - 22); + crypto_hash_write(ch, d_dd1, sizeof(d_dd1)); + + unsigned char *h2 = crypto_hash_read(ch); + if (!h2) { + crypto_hash_close(ch); + free(sdad); + return 1; + } + + if (verbose) { + printf("crypto hash2:\n"); + dump_buffer(h2, 20, stdout, 0); + } + + crypto_hash_close(ch); + + free(sdad); + + return 0; +} + +static int dda_test_pk(bool verbose) +{ + const struct emv_pk *pk = &mchip_05; + struct tlvdb *db; + + db = tlvdb_external(0x90, sizeof(d_issuer_cert), d_issuer_cert); + tlvdb_add(db, tlvdb_external(0x9f32, sizeof(d_issuer_exp), d_issuer_exp)); + tlvdb_add(db, tlvdb_external(0x92, sizeof(d_issuer_rem), d_issuer_rem)); + tlvdb_add(db, tlvdb_external(0x5a, sizeof(d_pan), d_pan)); + + struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db); + if (!ipk) { + fprintf(stderr, "Could not recover Issuer certificate!\n"); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_external(0x9f46, sizeof(d_icc_cert), d_icc_cert)); + tlvdb_add(db, tlvdb_external(0x9f47, sizeof(d_icc_exp), d_icc_exp)); + /*tlvdb_add(db, tlvdb_external(0x9f48, sizeof(d_issuer_rem), d_issuer_rem));*/ + + struct emv_pk *iccpk = emv_pki_recover_icc_cert(ipk, db, &ssd1_tlv); + if (!iccpk) { + fprintf(stderr, "Could not recover ICC certificate!\n"); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_external(0x9f4b, sizeof(d_sdad_cr), d_sdad_cr)); + + struct tlvdb *idndb = emv_pki_recover_idn(iccpk, db, &dd1_tlv); + if (!idndb) { + fprintf(stderr, "Could not recover IDN!\n"); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + const struct tlv *idn = tlvdb_get(idndb, 0x9f4c, NULL); + if (!idn) { + fprintf(stderr, "IDN not found!\n"); + tlvdb_free(idndb); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + if (verbose) { + printf("IDN:\n"); + dump_buffer(idn->value, idn->len, stdout, 0); + } + + tlvdb_free(idndb); + emv_pk_free(iccpk); + emv_pk_free(ipk); + tlvdb_free(db); + + return 0; +} + +int exec_dda_test(bool verbose) +{ + int ret; + fprintf(stdout, "\n"); + + ret = dda_test_raw(verbose); + if (ret) { + fprintf(stderr, "DDA raw test: failed\n"); + return ret; + } + fprintf(stdout, "DDA raw test: passed\n"); + + ret = dda_test_pk(verbose); + if (ret) { + fprintf(stderr, "DDA test pk: failed\n"); + return ret; + } + fprintf(stdout, "DDA test pk: passed\n"); + + return 0; +} diff --git a/client/emv/test/dda_test.h b/client/emv/test/dda_test.h new file mode 100644 index 00000000..354cd7e1 --- /dev/null +++ b/client/emv/test/dda_test.h @@ -0,0 +1,18 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#include + +extern int exec_dda_test(bool verbose); diff --git a/client/emv/test/sda_test.c b/client/emv/test/sda_test.c new file mode 100644 index 00000000..2aafcf19 --- /dev/null +++ b/client/emv/test/sda_test.c @@ -0,0 +1,277 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "../emv_pk.h" +#include "../crypto.h" +#include "../dump.h" +#include "../tlv.h" +#include "../emv_pki.h" + +#include +#include +#include + +struct emv_pk vsdc_01 = { + .rid = { 0xa0, 0x00, 0x00, 0x00, 0x03, }, + .index = 1, + .hash_algo = HASH_SHA_1, + .pk_algo = PK_RSA, + .hash = { + 0xd3, 0x4a, 0x6a, 0x77, + 0x60, 0x11, 0xc7, 0xe7, + 0xce, 0x3a, 0xec, 0x5f, + 0x03, 0xad, 0x2f, 0x8c, + 0xfc, 0x55, 0x03, 0xcc, }, + .exp = { 0x03, }, + .elen = 1, + .mlen = 1024 / 8, + .modulus = (unsigned char[]){ + 0xc6, 0x96, 0x03, 0x42, 0x13, 0xd7, 0xd8, 0x54, 0x69, 0x84, 0x57, 0x9d, 0x1d, 0x0f, 0x0e, 0xa5, + 0x19, 0xcf, 0xf8, 0xde, 0xff, 0xc4, 0x29, 0x35, 0x4c, 0xf3, 0xa8, 0x71, 0xa6, 0xf7, 0x18, 0x3f, + 0x12, 0x28, 0xda, 0x5c, 0x74, 0x70, 0xc0, 0x55, 0x38, 0x71, 0x00, 0xcb, 0x93, 0x5a, 0x71, 0x2c, + 0x4e, 0x28, 0x64, 0xdf, 0x5d, 0x64, 0xba, 0x93, 0xfe, 0x7e, 0x63, 0xe7, 0x1f, 0x25, 0xb1, 0xe5, + 0xf5, 0x29, 0x85, 0x75, 0xeb, 0xe1, 0xc6, 0x3a, 0xa6, 0x17, 0x70, 0x69, 0x17, 0x91, 0x1d, 0xc2, + 0xa7, 0x5a, 0xc2, 0x8b, 0x25, 0x1c, 0x7e, 0xf4, 0x0f, 0x23, 0x65, 0x91, 0x24, 0x90, 0xb9, 0x39, + 0xbc, 0xa2, 0x12, 0x4a, 0x30, 0xa2, 0x8f, 0x54, 0x40, 0x2c, 0x34, 0xae, 0xca, 0x33, 0x1a, 0xb6, + 0x7e, 0x1e, 0x79, 0xb2, 0x85, 0xdd, 0x57, 0x71, 0xb5, 0xd9, 0xff, 0x79, 0xea, 0x63, 0x0b, 0x75, + }, +}; + +const unsigned char issuer_cert[] = { + 0x3c, 0x5f, 0xea, 0xd4, 0xdd, 0x7b, 0xca, 0x44, 0xf9, 0x3e, 0x90, 0xc4, 0x4f, 0x76, 0xed, 0xe5, + 0x4a, 0x32, 0x88, 0xec, 0xdc, 0x78, 0x46, 0x9f, 0xcb, 0x12, 0x25, 0xc0, 0x3b, 0x2c, 0x04, 0xf2, + 0xc2, 0xf4, 0x12, 0x28, 0x1a, 0x08, 0x22, 0xdf, 0x14, 0x64, 0x92, 0x30, 0x98, 0x9f, 0xb1, 0x49, + 0x40, 0x70, 0xda, 0xf8, 0xc9, 0x53, 0x4a, 0x78, 0x81, 0x96, 0x01, 0x48, 0x61, 0x6a, 0xce, 0x58, + 0x17, 0x88, 0x12, 0x0d, 0x35, 0x06, 0xac, 0xe4, 0xce, 0xe5, 0x64, 0xfb, 0x27, 0xee, 0x53, 0x34, + 0x1c, 0x22, 0xf0, 0xb4, 0x5b, 0x31, 0x87, 0x3d, 0x05, 0xde, 0x54, 0x5e, 0xfe, 0x33, 0xbc, 0xd2, + 0x9b, 0x21, 0x85, 0xd0, 0x35, 0xa8, 0x06, 0xad, 0x08, 0xc6, 0x97, 0x6f, 0x35, 0x05, 0xa1, 0x99, + 0x99, 0x93, 0x0c, 0xa8, 0xa0, 0x3e, 0xfa, 0x32, 0x1c, 0x48, 0x60, 0x61, 0xf7, 0xdc, 0xec, 0x9f, +}; + +const unsigned char issuer_rem[] = { + 0x1e, 0xbc, 0xa3, 0x0f, 0x00, 0xce, 0x59, 0x62, 0xa8, 0xc6, 0xe1, 0x30, 0x54, 0x4b, 0x82, 0x89, + 0x1b, 0x23, 0x6c, 0x65, 0xde, 0x29, 0x31, 0x7f, 0x36, 0x47, 0x35, 0xde, 0xe6, 0x3f, 0x65, 0x98, + 0x97, 0x58, 0x35, 0xd5 +}; + +const unsigned char issuer_exp[] = { + 0x03, +}; + +const unsigned char ssad_cr[] = { + 0x99, 0xa5, 0x58, 0xb6, 0x2b, 0x67, 0x4a, 0xa5, 0xe7, 0xd2, 0xa5, 0x7e, 0x5e, 0xf6, 0xa6, 0xf2, + 0x25, 0x8e, 0x5d, 0xa0, 0x52, 0xd0, 0x5b, 0x54, 0xe5, 0xc1, 0x15, 0xff, 0x1c, 0xec, 0xf9, 0x4a, + 0xa2, 0xdf, 0x8f, 0x39, 0xa0, 0x1d, 0x71, 0xc6, 0x19, 0xeb, 0x81, 0x9d, 0xa5, 0x2e, 0xf3, 0x81, + 0xe8, 0x49, 0x79, 0x58, 0x6a, 0xea, 0x78, 0x55, 0xff, 0xbe, 0xf4, 0x0a, 0xa3, 0xa7, 0x1c, 0xd3, + 0xb0, 0x4c, 0xfd, 0xf2, 0x70, 0xae, 0xc8, 0x15, 0x8a, 0x27, 0x97, 0xf2, 0x4f, 0xd6, 0x13, 0xb7, + 0x48, 0x13, 0x46, 0x61, 0x13, 0x5c, 0xd2, 0x90, 0xe4, 0x5b, 0x04, 0xa8, 0xe0, 0xcc, 0xc7, 0x11, + 0xae, 0x04, 0x2f, 0x15, 0x9e, 0x73, 0xc8, 0x9c, 0x2a, 0x7e, 0x65, 0xa4, 0xc2, 0xfd, 0x1d, 0x61, + 0x06, 0x02, 0x4a, 0xa2, 0x71, 0x30, 0xb0, 0xec, 0xec, 0x02, 0x38, 0xf9, 0x16, 0x59, 0xde, 0x96, +}; + +const unsigned char ssd1[] = { + 0x5f, 0x24, 0x03, 0x08, 0x12, 0x31, 0x5a, 0x08, 0x42, 0x76, 0x55, 0x00, 0x13, 0x23, 0x45, 0x99, 0x5f, 0x34, 0x01, 0x01, 0x9f, 0x07, 0x02, 0xff, 0x00, 0x9f, 0x0d, 0x05, 0xd0, 0x40, 0xac, 0xa8, 0x00, 0x9f, 0x0e, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9f, 0x0f, 0x05, 0xd0, 0x68, 0xbc, 0xf8, 0x00, + 0x5c, 0x00, +}; +static const struct tlv ssd1_tlv = { + .len = sizeof(ssd1), + .value = ssd1, +}; + +const unsigned char pan[] = { + 0x42, 0x76, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static int sda_test_raw(bool verbose) +{ + const struct emv_pk *pk = &vsdc_01; + + struct crypto_pk *kcp = crypto_pk_open(PK_RSA, + pk->modulus, pk->mlen, + pk->exp, pk->elen); + if (!kcp) + return 1; + + unsigned char *ipk_data; + size_t ipk_data_len; + ipk_data = crypto_pk_encrypt(kcp, issuer_cert, sizeof(issuer_cert), &ipk_data_len); + crypto_pk_close(kcp); + + if (!ipk_data) + return 1; + + if (verbose) { + printf("issuer cert:\n"); + dump_buffer(ipk_data, ipk_data_len, stdout, 0); + } + + size_t ipk_pk_len = ipk_data[13]; + unsigned char *ipk_pk = malloc(ipk_pk_len); + memcpy(ipk_pk, ipk_data + 15, ipk_data_len - 36); + memcpy(ipk_pk + ipk_data_len - 36, issuer_rem, sizeof(issuer_rem)); + + struct crypto_hash *ch; + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_write(ch, ipk_data + 1, 14); + crypto_hash_write(ch, ipk_pk, ipk_pk_len); + crypto_hash_write(ch, issuer_exp, sizeof(issuer_exp)); + + unsigned char *h = crypto_hash_read(ch); + if (!h) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + if (verbose) { + printf("crypto hash:\n"); + dump_buffer(h, 20, stdout, 0); + } + + if (memcmp(ipk_data + ipk_data_len - 21, h, 20)) { + crypto_hash_close(ch); + free(ipk_pk); + free(ipk_data); + return 1; + } + + crypto_hash_close(ch); + free(ipk_data); + + struct crypto_pk *ikcp = crypto_pk_open(PK_RSA, ipk_pk, (int) ipk_pk_len, + issuer_exp, (int) sizeof(issuer_exp)); + free(ipk_pk); + if (!ikcp) + return 1; + + size_t ssad_len; + unsigned char *ssad = crypto_pk_encrypt(ikcp, ssad_cr, sizeof(ssad_cr), &ssad_len); + crypto_pk_close(ikcp); + if (!ssad) + return 1; + + if (verbose) { + printf("ssad:\n"); + dump_buffer(ssad, ssad_len, stdout, 0); + } + + ch = crypto_hash_open(HASH_SHA_1); + if (!ch) { + free(ssad); + return 1; + } + + crypto_hash_write(ch, ssad + 1, ssad_len - 22); + crypto_hash_write(ch, ssd1, sizeof(ssd1)); + + unsigned char *h2 = crypto_hash_read(ch); + if (!h2) { + crypto_hash_close(ch); + free(ssad); + return 1; + } + + if (verbose) { + printf("crypto hash2:\n"); + dump_buffer(h2, 20, stdout, 0); + } + + crypto_hash_close(ch); + + free(ssad); + + return 0; +} + +static int sda_test_pk(bool verbose) +{ + const struct emv_pk *pk = &vsdc_01; + struct tlvdb *db; + + db = tlvdb_external(0x90, sizeof(issuer_cert), issuer_cert); + tlvdb_add(db, tlvdb_external(0x9f32, sizeof(issuer_exp), issuer_exp)); + tlvdb_add(db, tlvdb_external(0x92, sizeof(issuer_rem), issuer_rem)); + tlvdb_add(db, tlvdb_external(0x5a, sizeof(pan), pan)); + + struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db); + if (!ipk) { + fprintf(stderr, "Could not recover Issuer certificate!\n"); + tlvdb_free(db); + return 2; + } + + tlvdb_add(db, tlvdb_external(0x93, sizeof(ssad_cr), ssad_cr)); + + struct tlvdb *dacdb = emv_pki_recover_dac(ipk, db, &ssd1_tlv); + if (!dacdb) { + fprintf(stderr, "Could not recover DAC!\n"); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + const struct tlv *dac = tlvdb_get(dacdb, 0x9f45, NULL); + if (!dac) { + fprintf(stderr, "DAC not found!\n"); + tlvdb_free(dacdb); + emv_pk_free(ipk); + tlvdb_free(db); + return 2; + } + + if (verbose) { + printf("dac:\n"); + dump_buffer(dac->value, dac->len, stdout, 0); + } + + tlvdb_free(dacdb); + emv_pk_free(ipk); + tlvdb_free(db); + + return 0; +} + +int exec_sda_test(bool verbose) +{ + int ret; + fprintf(stdout, "\n"); + + ret = sda_test_raw(verbose); + if (ret) { + fprintf(stderr, "SDA raw test: failed\n"); + return ret; + } + fprintf(stdout, "SDA raw test: passed\n"); + + ret = sda_test_pk(verbose); + if (ret) { + fprintf(stderr, "SDA test pk: failed\n"); + return ret; + } + fprintf(stdout, "SDA test pk: passed\n"); + + return 0; +} diff --git a/client/emv/test/sda_test.h b/client/emv/test/sda_test.h new file mode 100644 index 00000000..43a4c948 --- /dev/null +++ b/client/emv/test/sda_test.h @@ -0,0 +1,16 @@ +/* + * emv-tools - a set of tools to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +extern int exec_sda_test(bool verbose); diff --git a/client/emv/tlv.c b/client/emv/tlv.c new file mode 100644 index 00000000..540c33e4 --- /dev/null +++ b/client/emv/tlv.c @@ -0,0 +1,537 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * https://github.com/lumag/emv-tools/blob/master/lib/tlv.c + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "tlv.h" + +#include +#include +#include +#include + +#define TLV_TAG_CLASS_MASK 0xc0 +#define TLV_TAG_COMPLEX 0x20 +#define TLV_TAG_VALUE_MASK 0x1f +#define TLV_TAG_VALUE_CONT 0x1f +#define TLV_TAG_INVALID 0 + +#define TLV_LEN_LONG 0x80 +#define TLV_LEN_MASK 0x7f +#define TLV_LEN_INVALID (~0) + +// http://radek.io/2012/11/10/magical-container_of-macro/ +//#define container_of(ptr, type, member) ({ +// const typeof( ((type *)0)->member ) *__mptr = (ptr); +// (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct tlvdb { + struct tlv tag; + struct tlvdb *next; + struct tlvdb *parent; + struct tlvdb *children; +}; + +struct tlvdb_root { + struct tlvdb db; + size_t len; + unsigned char buf[0]; +}; + +static tlv_tag_t tlv_parse_tag(const unsigned char **buf, size_t *len) +{ + tlv_tag_t tag; + + if (*len == 0) + return TLV_TAG_INVALID; + tag = **buf; + --*len; + ++*buf; + if ((tag & TLV_TAG_VALUE_MASK) != TLV_TAG_VALUE_CONT) + return tag; + + if (*len == 0) + return TLV_TAG_INVALID; + + tag <<= 8; + tag |= **buf; + --*len; + ++*buf; + + return tag; +} + +static size_t tlv_parse_len(const unsigned char **buf, size_t *len) +{ + size_t l; + + if (*len == 0) + return TLV_LEN_INVALID; + + l = **buf; + --*len; + ++*buf; + + if (!(l & TLV_LEN_LONG)) + return l; + + size_t ll = l &~ TLV_LEN_LONG; + if (*len < ll) + return TLV_LEN_INVALID; + + /* FIXME */ + if (ll != 1) + return TLV_LEN_INVALID; + + l = **buf; + --*len; + ++*buf; + + return l; +} + +bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv) +{ + tlv->value = 0; + + tlv->tag = tlv_parse_tag(buf, len); + if (tlv->tag == TLV_TAG_INVALID) + return false; + + tlv->len = tlv_parse_len(buf, len); + if (tlv->len == TLV_LEN_INVALID) + return false; + + return true; +} + +static struct tlvdb *tlvdb_parse_children(struct tlvdb *parent); + +static bool tlvdb_parse_one(struct tlvdb *tlvdb, + struct tlvdb *parent, + const unsigned char **tmp, + size_t *left) +{ + tlvdb->next = tlvdb->children = NULL; + tlvdb->parent = parent; + + tlvdb->tag.tag = tlv_parse_tag(tmp, left); + if (tlvdb->tag.tag == TLV_TAG_INVALID) + goto err; + + tlvdb->tag.len = tlv_parse_len(tmp, left); + if (tlvdb->tag.len == TLV_LEN_INVALID) + goto err; + + if (tlvdb->tag.len > *left) + goto err; + + tlvdb->tag.value = *tmp; + + *tmp += tlvdb->tag.len; + *left -= tlvdb->tag.len; + + if (tlv_is_constructed(&tlvdb->tag) && (tlvdb->tag.len != 0)) { + tlvdb->children = tlvdb_parse_children(tlvdb); + if (!tlvdb->children) + goto err; + } else { + tlvdb->children = NULL; + } + + return true; + +err: + return false; +} + +static struct tlvdb *tlvdb_parse_children(struct tlvdb *parent) +{ + const unsigned char *tmp = parent->tag.value; + size_t left = parent->tag.len; + struct tlvdb *tlvdb, *first = NULL, *prev = NULL; + + while (left != 0) { + tlvdb = malloc(sizeof(*tlvdb)); + if (prev) + prev->next = tlvdb; + else + first = tlvdb; + prev = tlvdb; + + if (!tlvdb_parse_one(tlvdb, parent, &tmp, &left)) + goto err; + + tlvdb->parent = parent; + } + + return first; + +err: + tlvdb_free(first); + + return NULL; +} + +struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len) +{ + struct tlvdb_root *root; + const unsigned char *tmp; + size_t left; + + if (!len || !buf) + return NULL; + + root = malloc(sizeof(*root) + len); + root->len = len; + memcpy(root->buf, buf, len); + + tmp = root->buf; + left = len; + + if (!tlvdb_parse_one(&root->db, NULL, &tmp, &left)) + goto err; + + if (left) + goto err; + + return &root->db; + +err: + tlvdb_free(&root->db); + + return NULL; +} + +struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len) +{ + struct tlvdb_root *root; + const unsigned char *tmp; + size_t left; + + if (!len || !buf) + return NULL; + + root = malloc(sizeof(*root) + len); + root->len = len; + memcpy(root->buf, buf, len); + + tmp = root->buf; + left = len; + + if (!tlvdb_parse_one(&root->db, NULL, &tmp, &left)) + goto err; + + while (left != 0) { + struct tlvdb *db = malloc(sizeof(*db)); + if (!tlvdb_parse_one(db, NULL, &tmp, &left)) { + free (db); + goto err; + } + + tlvdb_add(&root->db, db); + } + + return &root->db; + +err: + tlvdb_free(&root->db); + + return NULL; +} + +struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb_root *root = malloc(sizeof(*root) + len); + + root->len = len; + memcpy(root->buf, value, len); + + root->db.parent = root->db.next = root->db.children = NULL; + root->db.tag.tag = tag; + root->db.tag.len = len; + root->db.tag.value = root->buf; + + return &root->db; +} + +struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb_root *root = malloc(sizeof(*root)); + + root->len = 0; + + root->db.parent = root->db.next = root->db.children = NULL; + root->db.tag.tag = tag; + root->db.tag.len = len; + root->db.tag.value = value; + + return &root->db; +} + +void tlvdb_free(struct tlvdb *tlvdb) +{ + struct tlvdb *next = NULL; + + if (!tlvdb) + return; + + for (; tlvdb; tlvdb = next) { + next = tlvdb->next; + tlvdb_free(tlvdb->children); + free(tlvdb); + } +} + +struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag) { + if (!tlvdb) + return NULL; + + return tlvdb_find(tlvdb->next, tag); +} + +struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag) { + if (!tlvdb) + return NULL; + + for (; tlvdb; tlvdb = tlvdb->next) { + if (tlvdb->tag.tag == tag) + return tlvdb; + } + + return NULL; +} + +struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag) { + if (!tlvdb) + return NULL; + + for (; tlvdb; tlvdb = tlvdb->next) { + if (tlvdb->tag.tag == tag) + return tlvdb; + + if (tlvdb->children) { + struct tlvdb * ch = tlvdb_find_full(tlvdb->children, tag); + if (ch) + return ch; + } + } + + return NULL; +} + +struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]) { + int i = 0; + struct tlvdb *tnext = tlvdb; + + while (tnext && tag[i]) { + tnext = tlvdb_find(tnext, tag[i]); + i++; + if (tag[i] && tnext) { + tnext = tnext->children; + } + } + + return tnext; +} + +void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) +{ + while (tlvdb->next) { + tlvdb = tlvdb->next; + } + + tlvdb->next = other; +} + +void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value) +{ + struct tlvdb *telm = tlvdb_find_full(tlvdb, tag); + if (telm == NULL) { + // new tlv element + tlvdb_add(tlvdb, tlvdb_fixed(tag, len, value)); + } else { + // the same tlv structure + if (telm->tag.tag == tag && telm->tag.len == len && !memcmp(telm->tag.value, value, len)) + return; + + // replace tlv element + struct tlvdb *tnewelm = tlvdb_fixed(tag, len, value); + tnewelm->next = telm->next; + tnewelm->parent = telm->parent; + + // if telm stayed first in children chain + if (telm->parent && telm->parent->children == telm) { + telm->parent->children = tnewelm; + } + + // if telm have previous element + if (telm != tlvdb) { + // elm in root + struct tlvdb *celm = tlvdb; + // elm in child list of node + if (telm->parent && telm->parent->children) + celm = telm->parent->children; + + // find previous element + for (; celm; celm = celm->next) { + if (celm->next == telm) { + celm->next = tnewelm; + break; + } + } + } + + // free old element with childrens + telm->next = NULL; + tlvdb_free(telm); + } + + return; +} + +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) +{ + struct tlvdb *next = NULL; + + if (!tlvdb) + return; + + for (; tlvdb; tlvdb = next) { + next = tlvdb->next; + cb(data, &tlvdb->tag, level, (tlvdb->children == NULL)); + tlvdb_visit(tlvdb->children, cb, data, level + 1); + } +} + +static const struct tlvdb *tlvdb_next(const struct tlvdb *tlvdb) +{ + if (tlvdb->children) + return tlvdb->children; + + while (tlvdb) { + if (tlvdb->next) + return tlvdb->next; + + tlvdb = tlvdb->parent; + } + + return NULL; +} + +const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev) +{ + if (prev) { +// tlvdb = tlvdb_next(container_of(prev, struct tlvdb, tag)); + tlvdb = tlvdb_next((struct tlvdb *)prev); + } + + + while (tlvdb) { + if (tlvdb->tag.tag == tag) + return &tlvdb->tag; + + tlvdb = tlvdb_next(tlvdb); + } + + return NULL; +} + +const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev) { + tlvdb = tlvdb->children; + return tlvdb_get(tlvdb, tag, prev); +} + +const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb) { + return &tlvdb->tag; +} + +unsigned char *tlv_encode(const struct tlv *tlv, size_t *len) +{ + size_t size = tlv->len; + unsigned char *data; + size_t pos; + + if (tlv->tag > 0x100) + size += 2; + else + size += 1; + + if (tlv->len > 0x7f) + size += 2; + else + size += 1; + + data = malloc(size); + if (!data) { + *len = 0; + return NULL; + } + + pos = 0; + + if (tlv->tag > 0x100) { + data[pos++] = tlv->tag >> 8; + data[pos++] = tlv->tag & 0xff; + } else + data[pos++] = tlv->tag; + + if (tlv->len > 0x7f) { + data[pos++] = 0x81; + data[pos++] = tlv->len; + } else + data[pos++] = tlv->len; + + memcpy(data + pos, tlv->value, tlv->len); + pos += tlv->len; + + *len = pos; + return data; +} + +bool tlv_is_constructed(const struct tlv *tlv) +{ + return (tlv->tag < 0x100 ? tlv->tag : tlv->tag >> 8) & TLV_TAG_COMPLEX; +} + +bool tlv_equal(const struct tlv *a, const struct tlv *b) +{ + if (!a && !b) + return true; + + if (!a || !b) + return false; + + 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) +{ + return tlvdb->next; +} + +struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb) +{ + return tlvdb->children; +} + +struct tlvdb *tlvdb_elm_get_parent(struct tlvdb *tlvdb) +{ + return tlvdb->parent; +} diff --git a/client/emv/tlv.h b/client/emv/tlv.h new file mode 100644 index 00000000..b25b51de --- /dev/null +++ b/client/emv/tlv.h @@ -0,0 +1,64 @@ +/* + * libopenemv - a library to work with EMV family of smart cards + * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * https://github.com/lumag/emv-tools/blob/master/lib/include/openemv/tlv.h + */ + +#ifndef TLV_H +#define TLV_H + +#include +#include +#include + +typedef uint16_t tlv_tag_t; + +struct tlv { + tlv_tag_t tag; + size_t len; + const unsigned char *value; +}; + +struct tlvdb; +typedef bool (*tlv_cb)(void *data, const struct tlv *tlv, int level, bool is_leaf); + +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 *tlvdb_parse(const unsigned char *buf, size_t len); +struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len); +void tlvdb_free(struct tlvdb *tlvdb); + +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_find_full(struct tlvdb *tlvdb, tlv_tag_t tag); // search also in childrens +struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag); +struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag); +struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]); + +void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other); +void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value); + +void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level); +const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); +const struct tlv *tlvdb_get_inchild(const struct tlvdb *tlvdb, tlv_tag_t tag, const struct tlv *prev); +const struct tlv *tlvdb_get_tlv(const struct tlvdb *tlvdb); + +bool tlv_parse_tl(const unsigned char **buf, size_t *len, struct tlv *tlv); +unsigned char *tlv_encode(const struct tlv *tlv, size_t *len); +bool tlv_is_constructed(const struct tlv *tlv); +bool tlv_equal(const struct tlv *a, const struct tlv *b); + +#endif diff --git a/client/flash.c b/client/flash.c index 7622e8a5..9a443cb8 100644 --- a/client/flash.c +++ b/client/flash.c @@ -20,14 +20,7 @@ #include "elf.h" #include "proxendian.h" #include "usb_cmd.h" - -void SendCommand(UsbCommand* txcmd); -void ReceiveCommand(UsbCommand* rxcmd); -void CloseProxmark(); -int OpenProxmark(size_t i); - -// FIXME: what the fuckity fuck -unsigned int current_command = CMD_UNKNOWN; +#include "comms.h" #define FLASH_START 0x100000 #define FLASH_SIZE (256*1024) @@ -44,9 +37,10 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; + // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed -static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, int num_phdrs) +static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, uint16_t num_phdrs) { Elf32_Phdr *phdr = phdrs; flash_seg_t *seg; @@ -192,12 +186,12 @@ static int check_segs(flash_file_t *ctx, int can_write_bl) { } // Load an ELF file and prepare it for flashing -int flash_load(flash_file_t *ctx, const char *name, int can_write_bl) +int flash_load(flash_file_t *ctx, const char *name, bool can_write_bl) { FILE *fd = NULL; Elf32_Ehdr ehdr; Elf32_Phdr *phdrs = NULL; - int num_phdrs; + uint16_t num_phdrs; int res; fd = fopen(name, "rb"); @@ -276,11 +270,11 @@ fail: // Get the state of the proxmark, backwards compatible static int get_proxmark_state(uint32_t *state) { - UsbCommand c; + UsbCommand c = {0}; c.cmd = CMD_DEVICE_INFO; - SendCommand(&c); + SendCommand(&c); UsbCommand resp; - ReceiveCommand(&resp); + WaitForResponse(CMD_UNKNOWN, &resp); // wait for any response. No timeout. // Three outcomes: // 1. The old bootrom code will ignore CMD_DEVICE_INFO, but respond with an ACK @@ -338,17 +332,18 @@ static int enter_bootloader(char *serial_port_name) SendCommand(&c); fprintf(stderr,"Press and hold down button NOW if your bootloader requires it.\n"); } - msleep(100); + + msleep(100); CloseProxmark(); - fprintf(stderr,"Waiting for Proxmark to reappear on %s",serial_port_name); - do { - sleep(1); - fprintf(stderr, "."); - } while (!OpenProxmark(0)); - fprintf(stderr," Found.\n"); - - return 0; + bool opened = OpenProxmark(serial_port_name, true, 120, true); // wait for 2 minutes + if (opened) { + fprintf(stderr," Found.\n"); + return 0; + } else { + fprintf(stderr,"Error: Proxmark not found.\n"); + return -1; + } } fprintf(stderr, "Error: Unknown Proxmark mode\n"); @@ -357,8 +352,8 @@ static int enter_bootloader(char *serial_port_name) static int wait_for_ack(void) { - UsbCommand ack; - ReceiveCommand(&ack); + UsbCommand ack; + WaitForResponse(CMD_UNKNOWN, &ack); if (ack.cmd != CMD_ACK) { printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", ack.cmd); return -1; @@ -407,12 +402,12 @@ static int write_block(uint32_t address, uint8_t *data, uint32_t length) memset(block_buf, 0xFF, BLOCK_SIZE); memcpy(block_buf, data, length); - UsbCommand c; + UsbCommand c; c.cmd = CMD_FINISH_WRITE; c.arg[0] = address; memcpy(c.d.asBytes, block_buf, length); - SendCommand(&c); - return wait_for_ack(); + SendCommand(&c); + return wait_for_ack(); } // Write a file's segments to Flash @@ -472,7 +467,7 @@ void flash_free(flash_file_t *ctx) // just reset the unit int flash_stop_flashing(void) { UsbCommand c = {CMD_HARDWARE_RESET}; - SendCommand(&c); - msleep(100); - return 0; + SendCommand(&c); + msleep(100); + return 0; } diff --git a/client/flash.h b/client/flash.h index 3e9f77a7..06c7c96e 100644 --- a/client/flash.h +++ b/client/flash.h @@ -10,7 +10,7 @@ #define __FLASH_H__ #include -#include "elf.h" +#include typedef struct { void *data; @@ -25,11 +25,10 @@ typedef struct { flash_seg_t *segments; } flash_file_t; -int flash_load(flash_file_t *ctx, const char *name, int can_write_bl); -int flash_start_flashing(int enable_bl_writes,char *serial_port_name); +int flash_load(flash_file_t *ctx, const char *name, bool can_write_bl); +int flash_start_flashing(int enable_bl_writes, char *serial_port_name); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); - #endif diff --git a/client/flasher.c b/client/flasher.c index f257d994..a008f7bc 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -10,21 +10,14 @@ #include #include #include +#include #include "proxmark3.h" #include "util.h" #include "util_posix.h" #include "flash.h" -#include "uart.h" +#include "comms.h" #include "usb_cmd.h" -#ifdef _WIN32 -# define unlink(x) -#else -# include -#endif - -static serial_port sp; -static char* serial_port_name; void cmd_debug(UsbCommand* UC) { // Debug @@ -40,62 +33,24 @@ void cmd_debug(UsbCommand* UC) { printf("...\n"); } -void SendCommand(UsbCommand* txcmd) { -// printf("send: "); -// cmd_debug(txcmd); - if (!uart_send(sp,(byte_t*)txcmd,sizeof(UsbCommand))) { - printf("Sending bytes to proxmark failed\n"); - exit(1); - } -} - -void ReceiveCommand(UsbCommand* rxcmd) { - byte_t* prxcmd = (byte_t*)rxcmd; - byte_t* prx = prxcmd; - size_t rxlen; - while (true) { - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-prxcmd), &rxlen)) { - prx += rxlen; - if ((prx-prxcmd) >= sizeof(UsbCommand)) { - return; - } - } - } -} - -void CloseProxmark() { - // Clean up the port - uart_close(sp); - // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* - unlink(serial_port_name); -} - -int OpenProxmark(size_t i) { - sp = uart_open(serial_port_name); - if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { - //poll once a second - return 0; - } - return 1; -} - static void usage(char *argv0) { fprintf(stderr, "Usage: %s [-b] image.elf [image.elf...]\n\n", argv0); fprintf(stderr, "\t-b\tEnable flashing of bootloader area (DANGEROUS)\n\n"); - //Is the example below really true? /Martin - fprintf(stderr, "Example:\n\n\t %s path/to/osimage.elf path/to/fpgaimage.elf\n", argv0); - fprintf(stderr, "\nExample (Linux):\n\n\t %s /dev/ttyACM0 armsrc/obj/fullimage.elf\n", argv0); - fprintf(stderr, "\nNote (Linux): if the flasher gets stuck in 'Waiting for Proxmark to reappear on ',\n"); - fprintf(stderr, " you need to blacklist proxmark for modem-manager - see wiki for more details:\n"); - fprintf(stderr, " http://code.google.com/p/proxmark3/wiki/Linux\n\n"); + fprintf(stderr, "\nExample:\n\n\t %s "SERIAL_PORT_H" armsrc/obj/fullimage.elf\n", argv0); +#ifdef __linux__ + fprintf(stderr, "\nNote (Linux): if the flasher gets stuck at 'Waiting for Proxmark to reappear',\n"); + fprintf(stderr, " you may need to blacklist proxmark for modem-manager. v1.4.14 and later\n"); + fprintf(stderr, " include this configuration patch already. The change can be found at:\n"); + fprintf(stderr, " https://cgit.freedesktop.org/ModemManager/ModemManager/commit/?id=6e7ff47\n\n"); +#endif } #define MAX_FILES 4 int main(int argc, char **argv) { - int can_write_bl = 0; + int can_write_bl = false; int num_files = 0; int res; flash_file_t files[MAX_FILES]; @@ -110,7 +65,7 @@ int main(int argc, char **argv) for (int i = 2; i < argc; i++) { if (argv[i][0] == '-') { if (!strcmp(argv[i], "-b")) { - can_write_bl = 1; + can_write_bl = true; } else { usage(argv[0]); return -1; @@ -126,16 +81,16 @@ int main(int argc, char **argv) } } - serial_port_name = argv[1]; - - fprintf(stderr,"Waiting for Proxmark to appear on %s",serial_port_name); - do { - msleep(1000); - fprintf(stderr, "."); - } while (!OpenProxmark(0)); - fprintf(stderr," Found.\n"); + char* serial_port_name = argv[1]; - res = flash_start_flashing(can_write_bl,serial_port_name); + if (!OpenProxmark(serial_port_name, true, 120, true)) { // wait for 2 minutes + fprintf(stderr, "Could not find Proxmark on %s.\n\n", serial_port_name); + return -1; + } else { + fprintf(stderr," Found.\n"); + } + + res = flash_start_flashing(can_write_bl, serial_port_name); if (res < 0) return -1; @@ -155,6 +110,7 @@ int main(int argc, char **argv) if (res < 0) return -1; + // Stop the command thread. CloseProxmark(); fprintf(stderr, "All done.\n\n"); diff --git a/client/fpga_compress.c b/client/fpga_compress.c index 2ef883db..418a02b8 100644 --- a/client/fpga_compress.c +++ b/client/fpga_compress.c @@ -1,4 +1,6 @@ //----------------------------------------------------------------------------- +// piwi, 2017, 2018 +// // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. @@ -11,9 +13,11 @@ #include #include +#include #include #include #include +#include "fpga.h" #include "zlib.h" #define MAX(a,b) ((a)>(b)?(a):(b)) @@ -37,18 +41,18 @@ #define COMPRESS_MAX_NICE_LENGTH 258 #define COMPRESS_MAX_CHAIN 8192 -#define FPGA_INTERLEAVE_SIZE 288 // (the FPGA's internal config frame size is 288 bits. Interleaving with 288 bytes should give best compression) -#define FPGA_CONFIG_SIZE 42336L // our current fpga_[lh]f.bit files are 42175 bytes. Rounded up to next multiple of FPGA_INTERLEAVE_SIZE #define HARDNESTED_TABLE_SIZE (sizeof(uint32_t) * ((1L<<19)+1)) static void usage(void) { - fprintf(stderr, "Usage: fpga_compress ... \n"); - fprintf(stderr, " Combine n FPGA bitstream files and compress them into one.\n\n"); - fprintf(stderr, " fpga_compress -d "); - fprintf(stderr, " Decompress . Write result to "); - fprintf(stderr, " fpga_compress -t "); - fprintf(stderr, " Compress hardnested table . Write result to "); + fprintf(stdout, "Usage: fpga_compress ... \n"); + fprintf(stdout, " Combine n FPGA bitstream files and compress them into one.\n\n"); + fprintf(stdout, " fpga_compress -v ... \n"); + fprintf(stdout, " Extract Version Information from FPGA bitstream files and write it to \n\n"); + fprintf(stdout, " fpga_compress -d \n"); + fprintf(stdout, " Decompress . Write result to \n\n"); + fprintf(stdout, " fpga_compress -t \n"); + fprintf(stdout, " Compress hardnested table . Write result to \n\n"); } @@ -60,7 +64,7 @@ static voidpf fpga_deflate_malloc(voidpf opaque, uInt items, uInt size) static void fpga_deflate_free(voidpf opaque, voidpf address) { - return free(address); + free(address); } @@ -108,7 +112,7 @@ int zlib_compress(FILE *infile[], uint8_t num_infiles, FILE *outfile, bool hardn for(uint16_t j = 0; j < num_infiles; j++) { for(uint16_t k = 0; k < FPGA_INTERLEAVE_SIZE; k++) { - c = fgetc(infile[j]); + c = (uint8_t)fgetc(infile[j]); if (!feof(infile[j])) { fpga_config[i++] = c; } else if (num_infiles > 1) { @@ -150,7 +154,7 @@ int zlib_compress(FILE *infile[], uint8_t num_infiles, FILE *outfile, bool hardn ret = deflate(&compressed_fpga_stream, Z_FINISH); } - fprintf(stderr, "compressed %u input bytes to %lu output bytes\n", i, compressed_fpga_stream.total_out); + fprintf(stdout, "compressed %u input bytes to %lu output bytes\n", i, compressed_fpga_stream.total_out); if (ret != Z_STREAM_END) { fprintf(stderr, "Error in deflate(): %i %s\n", ret, compressed_fpga_stream.msg); @@ -252,9 +256,162 @@ int zlib_decompress(FILE *infile, FILE *outfile) } +/* Simple Xilinx .bit parser. The file starts with the fixed opaque byte sequence + * 00 09 0f f0 0f f0 0f f0 0f f0 00 00 01 + * After that the format is 1 byte section type (ASCII character), 2 byte length + * (big endian), bytes content. Except for section 'e' which has 4 bytes + * length. + */ +static int bitparse_find_section(FILE *infile, char section_name, unsigned int *section_length) +{ + int result = 0; + #define MAX_FPGA_BIT_STREAM_HEADER_SEARCH 100 // maximum number of bytes to search for the requested section + uint16_t numbytes = 0; + while(numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH) { + char current_name = (char)fgetc(infile); + numbytes++; + if(current_name < 'a' || current_name > 'e') { + /* Strange section name, abort */ + break; + } + unsigned int current_length = 0; + switch(current_name) { + case 'e': + /* Four byte length field */ + current_length += fgetc(infile) << 24; + current_length += fgetc(infile) << 16; + numbytes += 2; + default: /* Fall through, two byte length field */ + current_length += fgetc(infile) << 8; + current_length += fgetc(infile) << 0; + numbytes += 2; + } + + if(current_name != 'e' && current_length > 255) { + /* Maybe a parse error */ + break; + } + + if(current_name == section_name) { + /* Found it */ + *section_length = current_length; + result = 1; + break; + } + + for (uint16_t i = 0; i < current_length && numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH; i++) { + (void)fgetc(infile); + numbytes++; + } + } + + return result; +} + + +static int FpgaGatherVersion(FILE *infile, char* infile_name, char *dst, int len) +{ + unsigned int fpga_info_len; + char tempstr[40] = {0x00}; + + dst[0] = '\0'; + + 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"); + return(EXIT_FAILURE); + } + } + + strncat(dst, basename(infile_name), len-1); + // if (bitparse_find_section(infile, 'a', &fpga_info_len)) { + // for (uint16_t i = 0; i < fpga_info_len; i++) { + // char c = (char)fgetc(infile); + // if (i < sizeof(tempstr)) { + // tempstr[i] = c; + // } + // } + // strncat(dst, tempstr, len-1); + // } + strncat(dst, " built", len-1); + if (bitparse_find_section(infile, 'b', &fpga_info_len)) { + strncat(dst, " for ", len-1); + for (uint16_t i = 0; i < fpga_info_len; i++) { + char c = (char)fgetc(infile); + if (i < sizeof(tempstr)) { + tempstr[i] = c; + } + } + strncat(dst, tempstr, len-1); + } + if (bitparse_find_section(infile, 'c', &fpga_info_len)) { + strncat(dst, " on ", len-1); + for (uint16_t i = 0; i < fpga_info_len; i++) { + char c = (char)fgetc(infile); + if (i < sizeof(tempstr)) { + tempstr[i] = c; + } + } + strncat(dst, tempstr, len-1); + } + if (bitparse_find_section(infile, 'd', &fpga_info_len)) { + strncat(dst, " at ", len-1); + for (uint16_t i = 0; i < fpga_info_len; i++) { + char c = (char)fgetc(infile); + if (i < sizeof(tempstr)) { + tempstr[i] = c; + } + } + strncat(dst, tempstr, len-1); + } + return 0; +} + + +static void print_version_info_preamble(FILE *outfile, int num_infiles) { + fprintf(outfile, "//-----------------------------------------------------------------------------\n"); + fprintf(outfile, "// piwi, 2018\n"); + fprintf(outfile, "//\n"); + fprintf(outfile, "// This code is licensed to you under the terms of the GNU GPL, version 2 or,\n"); + fprintf(outfile, "// at your option, any later version. See the LICENSE.txt file for the text of\n"); + fprintf(outfile, "// the license.\n"); + fprintf(outfile, "//-----------------------------------------------------------------------------\n"); + fprintf(outfile, "// Version information on fpga images\n"); + fprintf(outfile, "//\n"); + fprintf(outfile, "// This file is generated by fpga_compress. Don't edit!\n"); + fprintf(outfile, "//-----------------------------------------------------------------------------\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "\n"); + fprintf(outfile, "const int fpga_bitstream_num = %d;\n", num_infiles); + fprintf(outfile, "const char* const fpga_version_information[%d] = {\n", num_infiles); +} + + +static int generate_fpga_version_info(FILE *infile[], char *infile_names[], int num_infiles, FILE *outfile) { + + char version_string[80] = ""; + + print_version_info_preamble(outfile, num_infiles); + + for (int i = 0; i < num_infiles; i++) { + FpgaGatherVersion(infile[i], infile_names[i], version_string, sizeof(version_string)); + fprintf(outfile, "\t\"%s\"", version_string); + if (i != num_infiles-1) { + fprintf(outfile, ","); + } + fprintf(outfile,"\n"); + } + + fprintf(outfile, "};\n"); + + return 0; +} + + int main(int argc, char **argv) { FILE **infiles; + char **infile_names; FILE *outfile; if (argc == 1 || argc == 2) { @@ -271,43 +428,56 @@ int main(int argc, char **argv) } infiles[0] = fopen(argv[2], "rb"); if (infiles[0] == NULL) { - fprintf(stderr, "Error. Cannot open input file %s", argv[2]); + fprintf(stderr, "Error. Cannot open input file %s\n\n", argv[2]); return(EXIT_FAILURE); } outfile = fopen(argv[3], "wb"); if (outfile == NULL) { - fprintf(stderr, "Error. Cannot open output file %s", argv[3]); + fprintf(stderr, "Error. Cannot open output file %s\n\n", argv[3]); return(EXIT_FAILURE); } return zlib_decompress(infiles[0], outfile); - } else { // Compress + } else { // Compress or gemerate version info bool hardnested_mode = false; + bool generate_version_file = false; int num_input_files = 0; - if (!strcmp(argv[1], "-t")) { // hardnested table + if (!strcmp(argv[1], "-t")) { // compress one hardnested table if (argc != 4) { usage(); return(EXIT_FAILURE); } hardnested_mode = true; num_input_files = 1; - } else { + } else if (!strcmp(argv[1], "-v")) { // generate version info + generate_version_file = true; + num_input_files = argc-3; + } else { // compress 1..n fpga files num_input_files = argc-2; } + infiles = calloc(num_input_files, sizeof(FILE*)); - for (uint16_t i = 0; i < num_input_files; i++) { - infiles[i] = fopen(argv[i+hardnested_mode?2:1], "rb"); + infile_names = calloc(num_input_files, sizeof(char*)); + for (uint16_t i = 0; i < num_input_files; i++) { + infile_names[i] = argv[i+((hardnested_mode || generate_version_file)?2:1)]; + infiles[i] = fopen(infile_names[i], "rb"); if (infiles[i] == NULL) { - fprintf(stderr, "Error. Cannot open input file %s", argv[i+hardnested_mode?2:1]); + fprintf(stderr, "Error. Cannot open input file %s\n\n", infile_names[i]); return(EXIT_FAILURE); } } outfile = fopen(argv[argc-1], "wb"); if (outfile == NULL) { - fprintf(stderr, "Error. Cannot open output file %s", argv[argc-1]); + fprintf(stderr, "Error. Cannot open output file %s\n\n", argv[argc-1]); return(EXIT_FAILURE); } - return zlib_compress(infiles, num_input_files, outfile, hardnested_mode); + if (generate_version_file) { + if (generate_fpga_version_info(infiles, infile_names, num_input_files, outfile)) { + return(EXIT_FAILURE); + } + } else { + return zlib_compress(infiles, num_input_files, outfile, hardnested_mode); + } } } diff --git a/client/hardnested/hardnested_bf_core.c b/client/hardnested/hardnested_bf_core.c index 5e81c2ba..d02209e9 100644 --- a/client/hardnested/hardnested_bf_core.c +++ b/client/hardnested/hardnested_bf_core.c @@ -342,7 +342,7 @@ const uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, // } #endif // add the even state bits - const bitslice_t const *restrict bitsliced_even_state = bitsliced_even_states[block_idx]; + const bitslice_t *restrict bitsliced_even_state = bitsliced_even_states[block_idx]; for(uint32_t state_idx = 1; state_idx < STATE_SIZE; state_idx += 2) { state_p[state_idx] = bitsliced_even_state[state_idx/2]; } @@ -548,44 +548,105 @@ out: crack_states_bitsliced_t *crack_states_bitsliced_function_p = &crack_states_bitsliced_dispatch; bitslice_test_nonces_t *bitslice_test_nonces_function_p = &bitslice_test_nonces_dispatch; -// determine the available instruction set at runtime and call the correct function -const uint64_t crack_states_bitsliced_dispatch(uint32_t cuid, uint8_t *best_first_bytes, statelist_t *p, uint32_t *keys_found, uint64_t *num_keys_tested, uint32_t nonces_to_bruteforce, uint8_t *bf_test_nonce_2nd_byte, noncelist_t *nonces) { +static SIMDExecInstr intSIMDInstr = SIMD_AUTO; + +void SetSIMDInstr(SIMDExecInstr instr) { + intSIMDInstr = instr; + + crack_states_bitsliced_function_p = &crack_states_bitsliced_dispatch; + bitslice_test_nonces_function_p = &bitslice_test_nonces_dispatch; +} + +SIMDExecInstr GetSIMDInstr() { + SIMDExecInstr instr = SIMD_NONE; + #if defined (__i386__) || defined (__x86_64__) #if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) #if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) - if (__builtin_cpu_supports("avx512f")) crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX512; - else if (__builtin_cpu_supports("avx2")) crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX2; + if (__builtin_cpu_supports("avx512f")) instr = SIMD_AVX512; + else if (__builtin_cpu_supports("avx2")) instr = SIMD_AVX2; #else - if (__builtin_cpu_supports("avx2")) crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX2; + if (__builtin_cpu_supports("avx2")) instr = SIMD_AVX2; #endif - else if (__builtin_cpu_supports("avx")) crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX; - else if (__builtin_cpu_supports("sse2")) crack_states_bitsliced_function_p = &crack_states_bitsliced_SSE2; - else if (__builtin_cpu_supports("mmx")) crack_states_bitsliced_function_p = &crack_states_bitsliced_MMX; + else if (__builtin_cpu_supports("avx")) instr = SIMD_AVX; + else if (__builtin_cpu_supports("sse2")) instr = SIMD_SSE2; + else if (__builtin_cpu_supports("mmx")) instr = SIMD_MMX; else #endif #endif - crack_states_bitsliced_function_p = &crack_states_bitsliced_NOSIMD; + instr = SIMD_NONE; + + return instr; +} + +SIMDExecInstr GetSIMDInstrAuto() { + SIMDExecInstr instr = intSIMDInstr; + if (instr == SIMD_AUTO) + return GetSIMDInstr(); + + return instr; +} + +// determine the available instruction set at runtime and call the correct function +const uint64_t crack_states_bitsliced_dispatch(uint32_t cuid, uint8_t *best_first_bytes, statelist_t *p, uint32_t *keys_found, uint64_t *num_keys_tested, uint32_t nonces_to_bruteforce, uint8_t *bf_test_nonce_2nd_byte, noncelist_t *nonces) { + switch(GetSIMDInstrAuto()) { +#if defined (__i386__) || defined (__x86_64__) +#if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) +#if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) + case SIMD_AVX512: + crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX512; + break; +#endif + case SIMD_AVX2: + crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX2; + break; + case SIMD_AVX: + crack_states_bitsliced_function_p = &crack_states_bitsliced_AVX; + break; + case SIMD_SSE2: + crack_states_bitsliced_function_p = &crack_states_bitsliced_SSE2; + break; + case SIMD_MMX: + crack_states_bitsliced_function_p = &crack_states_bitsliced_MMX; + break; +#endif +#endif + default: + crack_states_bitsliced_function_p = &crack_states_bitsliced_NOSIMD; + break; + } // call the most optimized function for this CPU return (*crack_states_bitsliced_function_p)(cuid, best_first_bytes, p, keys_found, num_keys_tested, nonces_to_bruteforce, bf_test_nonce_2nd_byte, nonces); } void bitslice_test_nonces_dispatch(uint32_t nonces_to_bruteforce, uint32_t *bf_test_nonce, uint8_t *bf_test_nonce_par) { -#if defined (__i386__) || defined (__x86_64__) - #if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) - #if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) - if (__builtin_cpu_supports("avx512f")) bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX512; - else if (__builtin_cpu_supports("avx2")) bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX2; - #else - if (__builtin_cpu_supports("avx2")) bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX2; - #endif - else if (__builtin_cpu_supports("avx")) bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX; - else if (__builtin_cpu_supports("sse2")) bitslice_test_nonces_function_p = &bitslice_test_nonces_SSE2; - else if (__builtin_cpu_supports("mmx")) bitslice_test_nonces_function_p = &bitslice_test_nonces_MMX; - else - #endif + switch(GetSIMDInstrAuto()) { +#if defined (__i386__) || defined (__x86_64__) +#if !defined(__APPLE__) || (defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) +#if (__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2) + case SIMD_AVX512: + bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX512; + break; #endif - bitslice_test_nonces_function_p = &bitslice_test_nonces_NOSIMD; + case SIMD_AVX2: + bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX2; + break; + case SIMD_AVX: + bitslice_test_nonces_function_p = &bitslice_test_nonces_AVX; + break; + case SIMD_SSE2: + bitslice_test_nonces_function_p = &bitslice_test_nonces_SSE2; + break; + case SIMD_MMX: + bitslice_test_nonces_function_p = &bitslice_test_nonces_MMX; + break; +#endif +#endif + default: + bitslice_test_nonces_function_p = &bitslice_test_nonces_NOSIMD; + break; + } // call the most optimized function for this CPU (*bitslice_test_nonces_function_p)(nonces_to_bruteforce, bf_test_nonce, bf_test_nonce_par); diff --git a/client/hardnested/hardnested_bf_core.h b/client/hardnested/hardnested_bf_core.h index 7a445993..b3df0547 100644 --- a/client/hardnested/hardnested_bf_core.h +++ b/client/hardnested/hardnested_bf_core.h @@ -52,6 +52,18 @@ THE SOFTWARE. #include "hardnested_bruteforce.h" // statelist_t +typedef enum { + SIMD_AUTO, + SIMD_AVX512, + SIMD_AVX2, + SIMD_AVX, + SIMD_SSE2, + SIMD_MMX, + SIMD_NONE, +} SIMDExecInstr; +extern void SetSIMDInstr(SIMDExecInstr instr); +extern SIMDExecInstr GetSIMDInstrAuto(); + extern const uint64_t crack_states_bitsliced(uint32_t cuid, uint8_t *best_first_bytes, statelist_t *p, uint32_t *keys_found, uint64_t *num_keys_tested, uint32_t nonces_to_bruteforce, uint8_t *bf_test_nonces_2nd_byte, noncelist_t *nonces); extern void bitslice_test_nonces(uint32_t nonces_to_bruteforce, uint32_t *bf_test_nonces, uint8_t *bf_test_nonce_par); diff --git a/client/hardnested/hardnested_bruteforce.c b/client/hardnested/hardnested_bruteforce.c index 718b7c5d..deea69d8 100644 --- a/client/hardnested/hardnested_bruteforce.c +++ b/client/hardnested/hardnested_bruteforce.c @@ -139,8 +139,13 @@ bool verify_key(uint32_t cuid, noncelist_t *nonces, uint8_t *best_first_bytes, u return true; } - -static void* crack_states_thread(void* x){ +static void* +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +crack_states_thread(void* x){ struct arg { bool silent; diff --git a/client/hid-flasher/proxusb.c b/client/hid-flasher/proxusb.c index 04dbb784..364b21a3 100644 --- a/client/hid-flasher/proxusb.c +++ b/client/hid-flasher/proxusb.c @@ -31,7 +31,6 @@ usb_dev_handle *devh = NULL; static unsigned int claimed_iface = 0; unsigned char return_on_error = 0; unsigned char error_occured = 0; -extern unsigned int current_command; void SendCommand(UsbCommand *c) { @@ -40,7 +39,6 @@ void SendCommand(UsbCommand *c) #if 0 printf("Sending %d bytes\n", sizeof(UsbCommand)); #endif - current_command = c->cmd; ret = usb_bulk_write(devh, 0x01, (char*)c, sizeof(UsbCommand), 1000); if (ret<0) { error_occured = 1; diff --git a/client/hidcardformats.c b/client/hidcardformats.c new file mode 100644 index 00000000..11d0558e --- /dev/null +++ b/client/hidcardformats.c @@ -0,0 +1,609 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// HID card format packing/unpacking routines +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "cmddata.h" +#include "hidcardformats.h" +#include "hidcardformatutils.h" +#include "parity.h" // for parity +#include "ui.h" + +bool Pack_H10301(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 26; // Set number of bits + packed->bot |= (card->CardNumber & 0xFFFF) << 1; + packed->bot |= (card->FacilityCode & 0xFF) << 17; + packed->bot |= oddparity32((packed->bot >> 1) & 0xFFF) & 1; + packed->bot |= (evenparity32((packed->bot >> 13) & 0xFFF) & 1) << 25; + return add_HID_header(packed); +} +bool Unpack_H10301(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 26) return false; // Wrong length? Stop here. + card->CardNumber = (packed->bot >> 1) & 0xFFFF; + card->FacilityCode = (packed->bot >> 17) & 0xFF; + card->ParityValid = + (oddparity32((packed->bot >> 1) & 0xFFF) == (packed->bot & 1)) && + ((evenparity32((packed->bot >> 13) & 0xFFF) & 1) == ((packed->bot >> 25) & 1)); + return true; +} + +bool Pack_Tecom27(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 27; + set_nonlinear_field(packed, card->FacilityCode, 10, (uint8_t[]){15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); + set_nonlinear_field(packed, card->CardNumber, 16, (uint8_t[]){0, 1, 13, 12, 9, 26, 20, 16, 17, 21, 25, 7, 8, 11, 4, 5}); + return add_HID_header(packed); +} +bool Unpack_Tecom27(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 27) return false; // Wrong length? Stop here. + 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, 10, (uint8_t[]){15, 19, 24, 23, 22, 18, 6, 10, 14, 3, 2}); + return true; +} + +bool Pack_2804W(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 28; + set_linear_field(packed, card->FacilityCode, 4, 8); + set_linear_field(packed, card->CardNumber, 12, 15); + set_bit_by_position(packed, + oddparity32(get_nonlinear_field(packed, 16, (uint8_t[]){4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26})) + , 2); + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 13)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 0, 27)) + , 27); + return add_HID_header(packed); +} +bool Unpack_2804W(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 28) return false; // Wrong length? Stop here. + card->FacilityCode = get_linear_field(packed, 4, 8); + card->CardNumber = get_linear_field(packed, 12, 15); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 13))) && + (get_bit_by_position(packed, 2) == oddparity32(get_nonlinear_field(packed, 16, (uint8_t[]){4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26}))) && + (get_bit_by_position(packed, 27) == oddparity32(get_linear_field(packed, 0, 27))); + return true; +} + +bool Pack_ATSW30(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 30; + set_linear_field(packed, card->FacilityCode, 1, 12); + set_linear_field(packed, card->CardNumber, 13, 16); + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 12)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 13, 16)) + , 29); + return add_HID_header(packed); +} +bool Unpack_ATSW30(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 30) return false; // Wrong length? Stop here. + card->FacilityCode = get_linear_field(packed, 1, 12); + card->CardNumber = get_linear_field(packed, 13, 16); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 12))) && + (get_bit_by_position(packed, 29) == oddparity32(get_linear_field(packed, 13, 16))); + return true; +} +bool Pack_ADT31(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 31; + set_linear_field(packed, card->FacilityCode, 1, 4); + set_linear_field(packed, card->CardNumber, 5, 23); + // Parity not known, but 4 bits are unused. + return add_HID_header(packed); +} +bool Unpack_ADT31(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 31) return false; // Wrong length? Stop here. + card->FacilityCode = get_linear_field(packed, 1, 4); + card->CardNumber = get_linear_field(packed, 5, 23); + return true; +} + +bool Pack_Kastle(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 32; // Set number of bits + set_bit_by_position(packed, 1, 1); // Always 1 + set_linear_field(packed, card->IssueLevel, 2, 5); + set_linear_field(packed, card->FacilityCode, 7, 8); + set_linear_field(packed, card->CardNumber, 15, 16); + set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 16)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 14, 17)), 31); + return add_HID_header(packed); +} +bool Unpack_Kastle(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_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 + card->IssueLevel = get_linear_field(packed, 2, 5); + card->FacilityCode = get_linear_field(packed, 7, 8); + card->CardNumber = get_linear_field(packed, 15, 16); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 16))) && + (get_bit_by_position(packed, 31) == oddparity32(get_linear_field(packed, 14, 17))); + return true; +} + +bool Pack_D10202(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 33; // Set number of bits + set_linear_field(packed, card->FacilityCode, 1, 7); + set_linear_field(packed, card->CardNumber, 8, 24); + set_bit_by_position(packed, evenparity32(get_linear_field(packed, 1, 16)), 0); + set_bit_by_position(packed, oddparity32(get_linear_field(packed, 16, 16)), 32); + return add_HID_header(packed); +} +bool Unpack_D10202(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 33) return false; // Wrong length? Stop here. + card->CardNumber = get_linear_field(packed, 8, 24); + card->FacilityCode = get_linear_field(packed, 1, 7); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 16))) && + (get_bit_by_position(packed, 32) == oddparity32(get_linear_field(packed, 16, 16))); + return true; +} + +bool Pack_H10306(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 34; // Set number of bits + packed->bot |= (card->CardNumber & 0xFFFF) << 1; + packed->bot |= (card->FacilityCode & 0x7FFF) << 17; + packed->mid |= (card->FacilityCode & 0x8000) >> 15; + packed->mid |= (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xFFFE0000)) & 1) << 1; + packed->bot |= ( oddparity32(packed->bot & 0x0001FFFE) & 1); + return add_HID_header(packed); +} +bool Unpack_H10306(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 34) return false; // Wrong length? Stop here. + card->CardNumber = (packed->bot >> 1) & 0xFFFF; + card->FacilityCode = ((packed->mid & 1) << 15) | ((packed->bot >> 17) & 0xFF); + card->ParityValid = + ((evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xFFFE0000)) & 1) == ((packed->mid >> 1) & 1)) && + ((oddparity32(packed->bot & 0x0001FFFE) & 1) == ((packed->bot & 1))); + return true; +} +bool Pack_N10002(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 34; // Set number of bits + set_linear_field(packed, card->FacilityCode, 9, 8); + set_linear_field(packed, card->CardNumber, 17, 16); + return add_HID_header(packed); +} +bool Unpack_N10002(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 34) return false; // Wrong length? Stop here. + card->CardNumber = get_linear_field(packed, 17, 16); + card->FacilityCode = get_linear_field(packed, 9, 8); + return true; +} + +bool Pack_C1k35s(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 35; // Set number of bits + packed->bot |= (card->CardNumber & 0x000FFFFF) << 1; + packed->bot |= (card->FacilityCode & 0x000007FF) << 21; + packed->mid |= (card->FacilityCode & 0x00000800) >> 11; + packed->mid |= (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xB6DB6DB6)) & 1) << 1; + packed->bot |= ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0x6DB6DB6C)) & 1); + packed->mid |= ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0xFFFFFFFF)) & 1) << 2; + return add_HID_header(packed); +} +bool Unpack_C1k35s(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 35) return false; // Wrong length? Stop here. + card->CardNumber = (packed->bot >> 1) & 0x000FFFFF; + card->FacilityCode = ((packed->mid & 1) << 11) | ((packed->bot >> 21)); + card->ParityValid = + (evenparity32((packed->mid & 0x00000001) ^ (packed->bot & 0xB6DB6DB6)) == ((packed->mid >> 1) & 1)) && + ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0x6DB6DB6C)) == ((packed->bot >> 0) & 1)) && + ( oddparity32((packed->mid & 0x00000003) ^ (packed->bot & 0xFFFFFFFF)) == ((packed->mid >> 2) & 1)); + return true; +} + +bool Pack_H10320(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 36; // Set number of bits + // This card is BCD-encoded rather than binary. Set the 4-bit groups independently. + for (uint32_t idx = 0; idx < 8; idx++){ + set_linear_field(packed, (uint64_t)(card->CardNumber / pow(10, 7-idx)) % 10, idx * 4, 4); + } + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}) + ), 32); + set_bit_by_position(packed, oddparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){1, 5, 9, 13, 17, 21, 25, 29}) + ), 33); + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){2, 6, 10, 14, 18, 22, 28, 30}) + ), 34); + set_bit_by_position(packed, evenparity32( + get_nonlinear_field(packed, 8, (uint8_t[]){3, 7, 11, 15, 19, 23, 29, 31}) + ), 35); + return add_HID_header(packed); +} +bool Unpack_H10320(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 36) return false; // Wrong length? Stop here. + // 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); + if (val > 9){ + // Violation of BCD; Zero and exit. + card->CardNumber = 0; + return false; + } else { + card->CardNumber += val * pow(10, 7-idx); + } + } + card->ParityValid = + (get_bit_by_position(packed, 32) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){0, 4, 8, 12, 16, 20, 24, 28}))) && + (get_bit_by_position(packed, 33) == oddparity32(get_nonlinear_field(packed, 8, (uint8_t[]){1, 5, 9, 13, 17, 21, 25, 29}))) && + (get_bit_by_position(packed, 34) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){2, 6, 10, 14, 18, 22, 28, 30}))) && + (get_bit_by_position(packed, 35) == evenparity32(get_nonlinear_field(packed, 8, (uint8_t[]){3, 7, 11, 15, 19, 23, 29, 31}))); + return true; +} + +bool Pack_S12906(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 36; // Set number of bits + set_linear_field(packed, card->FacilityCode, 1, 8); + set_linear_field(packed, card->IssueLevel, 9, 2); + set_linear_field(packed, card->CardNumber, 11, 24); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 1, 17)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 17, 18)) + , 35); + return add_HID_header(packed); +} +bool Unpack_S12906(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 36) return false; // Wrong length? Stop here. + card->FacilityCode = get_linear_field(packed, 1, 8); + card->IssueLevel = get_linear_field(packed, 9, 2); + card->CardNumber = get_linear_field(packed, 11, 24); + card->ParityValid = + (get_bit_by_position(packed, 0) == oddparity32(get_linear_field(packed, 1, 17))) && + (get_bit_by_position(packed, 35) == oddparity32(get_linear_field(packed, 17, 18))); + return true; +} + +bool Pack_Sie36(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 36; // Set number of bits + set_linear_field(packed, card->FacilityCode, 1, 18); + set_linear_field(packed, card->CardNumber, 19, 16); + set_bit_by_position(packed, + 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})) + , 0); + set_bit_by_position(packed, + 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})) + , 35); + return add_HID_header(packed); +} +bool Unpack_Sie36(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 36) return false; // Wrong length? Stop here. + 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}))); + return true; +} + +bool Pack_C15001(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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. + packed->Length = 36; // Set number of bits + set_linear_field(packed, card->OEM, 1, 10); + set_linear_field(packed, card->FacilityCode, 11, 8); + set_linear_field(packed, card->CardNumber, 19, 16); + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 17)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 18, 17)) + , 35); + return add_HID_header(packed); +} +bool Unpack_C15001(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_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); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 17))) && + (get_bit_by_position(packed, 35) == oddparity32(get_linear_field(packed, 18, 17))); + return true; +} + +bool Pack_H10302(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 37; // Set number of bits + set_linear_field(packed, card->CardNumber, 1, 35); + set_bit_by_position(packed, + evenparity32(get_linear_field(packed, 1, 18)) + , 0); + set_bit_by_position(packed, + oddparity32(get_linear_field(packed, 18, 18)) + , 36); + return add_HID_header(packed); +} +bool Unpack_H10302(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 37) return false; // Wrong length? Stop here. + card->CardNumber = get_linear_field(packed, 1, 35); + card->ParityValid = + (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 18))) && + (get_bit_by_position(packed, 36) == oddparity32(get_linear_field(packed, 18, 18))); + return true; +} + +bool Pack_H10304(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 37; // Set number of bits + packed->bot |= (card->CardNumber & 0x0007FFFF) << 1; + packed->bot |= (card->FacilityCode & 0x00000FFF) << 20; + packed->mid |= (card->FacilityCode & 0x0000F000) >> 12; + packed->mid |= (evenparity32((packed->mid & 0x0000000F) ^ (packed->bot & 0xFFFC0000)) & 1) << 4; + packed->bot |= ( oddparity32(packed->bot & 0x0007FFFE) & 1); + return add_HID_header(packed); +} +bool Unpack_H10304(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 37) return false; // Wrong length? Stop here. + card->CardNumber = (packed->bot >> 1) & 0x0007FFFF; + card->FacilityCode = ((packed->mid & 0xF) << 12) | ((packed->bot >> 20)); + card->ParityValid = + (evenparity32((packed->mid & 0x0000000F) ^ (packed->bot & 0xFFFC0000)) == ((packed->mid >> 4) & 1)) && + (oddparity32( packed->bot & 0x0007FFFE) == (packed->bot & 1)); + return true; +} + +bool Pack_P10001(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 40; // Set number of bits + set_linear_field(packed, 0xF, 0, 4); + set_linear_field(packed, card->FacilityCode, 4, 12); + set_linear_field(packed, card->CardNumber, 16, 16); + set_linear_field(packed, + get_linear_field(packed, 0, 8) ^ + get_linear_field(packed, 8, 8) ^ + get_linear_field(packed, 16, 8) ^ + get_linear_field(packed, 24, 8) + , 32, 8); + return add_HID_header(packed); +} +bool Unpack_P10001(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 40) return false; // Wrong length? Stop here. + card->CardNumber = get_linear_field(packed, 16, 16); + card->FacilityCode = get_linear_field(packed, 4, 12); + card->ParityValid = ( + get_linear_field(packed, 0, 8) ^ + get_linear_field(packed, 8, 8) ^ + get_linear_field(packed, 16, 8) ^ + get_linear_field(packed, 24, 8) + ) == get_linear_field(packed, 32, 8); + return true; +} + +bool Pack_C1k48s(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_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 + packed->Length = 48; // Set number of bits + packed->bot |= (card->CardNumber & 0x007FFFFF) << 1; + packed->bot |= (card->FacilityCode & 0x000000FF) << 24; + packed->mid |= (card->FacilityCode & 0x003FFF00) >> 8; + packed->mid |= (evenparity32((packed->mid & 0x00001B6D) ^ (packed->bot & 0xB6DB6DB6)) & 1) << 14; + packed->bot |= ( oddparity32((packed->mid & 0x000036DB) ^ (packed->bot & 0x6DB6DB6C)) & 1); + packed->mid |= ( oddparity32((packed->mid & 0x00007FFF) ^ (packed->bot & 0xFFFFFFFF)) & 1) << 15; + return add_HID_header(packed); +} +bool Unpack_C1k48s(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card){ + memset(card, 0, sizeof(hidproxcard_t)); + if (packed->Length != 48) return false; // Wrong length? Stop here. + card->CardNumber = (packed->bot >> 1) & 0x007FFFFF; + card->FacilityCode = ((packed->mid & 0x00003FFF) << 8) | ((packed->bot >> 24)); + card->ParityValid = + (evenparity32((packed->mid & 0x00001B6D) ^ (packed->bot & 0xB6DB6DB6)) == ((packed->mid >> 14) & 1)) && + ( oddparity32((packed->mid & 0x000036DB) ^ (packed->bot & 0x6DB6DB6C)) == ((packed->bot >> 0) & 1)) && + ( oddparity32((packed->mid & 0x00007FFF) ^ (packed->bot & 0xFFFFFFFF)) == ((packed->mid >> 15) & 1)); + return true; +} + +static const hidcardformat_t FormatTable[] = { + {"H10301", Pack_H10301, Unpack_H10301, "HID H10301 26-bit", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand", {1, 1, 0, 0, 1}}, // 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, 1}}, // 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 + {"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, "HID N10002 34-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au + {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit standard layout", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {"C15001", Pack_C15001, Unpack_C15001, "HID KeySpan 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 36-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}}, // imported from old pack/unpack + {"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit"}, // from cardinfo.barkweb.com.au + {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit standard layout", {1, 1, 0, 0, 1}}, // imported from old pack/unpack + {NULL, NULL, NULL, NULL, {0, 0, 0, 0, 0}} // Must null terminate array +}; + +void HIDListFormats(){ + if (FormatTable[0].Name == NULL) + return; + int i = 0; + PrintAndLog("%-10s %s", "Name", "Description"); + PrintAndLog("------------------------------------------------------------"); + while (FormatTable[i].Name) + { + PrintAndLog("%-10s %-30s", FormatTable[i].Name, FormatTable[i].Descrp); + ++i; + } + PrintAndLog(""); + return; +} + +hidcardformat_t HIDGetCardFormat(int idx){ + return FormatTable[idx]; +} + +int HIDFindCardFormat(const char *format) +{ + if (FormatTable[0].Name == NULL) + return -1; + int i = 0; + while (FormatTable[i].Name && strcmp(FormatTable[i].Name, format)) + { + ++i; + } + + if (FormatTable[i].Name) { + return i; + } else { + return -1; + } +} + +bool HIDPack(/* in */int FormatIndex, /* in */hidproxcard_t* card, /* out */hidproxmessage_t* packed){ + memset(packed, 0, sizeof(hidproxmessage_t)); + if (FormatIndex < 0 || FormatIndex >= (sizeof(FormatTable) / sizeof(FormatTable[0]))) return false; + return FormatTable[FormatIndex].Pack(card, packed); +} + +void HIDDisplayUnpackedCard(hidproxcard_t* card, const hidcardformat_t format){ + PrintAndLog(" Format: %s (%s)", format.Name, format.Descrp); + if (format.Fields.hasFacilityCode) + PrintAndLog("Facility Code: %d",card->FacilityCode); + if (format.Fields.hasCardNumber) + PrintAndLog(" Card Number: %d",card->CardNumber); + if (format.Fields.hasIssueLevel) + PrintAndLog(" Issue Level: %d",card->IssueLevel); + if (format.Fields.hasOEMCode) + PrintAndLog(" OEM Code: %d",card->OEM); + if (format.Fields.hasParity) + PrintAndLog(" Parity: %s",card->ParityValid ? "Valid" : "Invalid"); +} + +bool HIDTryUnpack(/* in */hidproxmessage_t* packed, /* in */bool ignoreParity){ + if (FormatTable[0].Name == NULL) + return false; + + bool result = false; + int i = 0; + hidproxcard_t card; + memset(&card, 0, sizeof(hidproxcard_t)); + while (FormatTable[i].Name) + { + if (FormatTable[i].Unpack(packed, &card)){ + if (ignoreParity || !FormatTable[i].Fields.hasParity || card.ParityValid){ + if (!result) PrintAndLog("--------------------------------------------------"); + result = true; + HIDDisplayUnpackedCard(&card, FormatTable[i]); + PrintAndLog("--------------------------------------------------"); + } + } + ++i; + } + return result; +} diff --git a/client/hidcardformats.h b/client/hidcardformats.h new file mode 100644 index 00000000..3019b823 --- /dev/null +++ b/client/hidcardformats.h @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// HID card format packing/unpacking routines +//----------------------------------------------------------------------------- + +#ifndef HIDCARDFORMATS_H__ +#define HIDCARDFORMATS_H__ + +#include +#include +#include "hidcardformatutils.h" + + +typedef struct hidcardformatdescriptor_s{ + bool hasCardNumber; + bool hasFacilityCode; + bool hasIssueLevel; + bool hasOEMCode; + bool hasParity; +} hidcardformatdescriptor_t; + +// Structure for defined HID card formats available for packing/unpacking +typedef struct hidcardformat_s{ + const char* Name; + bool (*Pack)(/*in*/hidproxcard_t* card, /*out*/hidproxmessage_t* packed); + bool (*Unpack)(/*in*/hidproxmessage_t* packed, /*out*/hidproxcard_t* card); + const char* Descrp; + hidcardformatdescriptor_t Fields; +} hidcardformat_t; + +void HIDListFormats(); +int HIDFindCardFormat(const char *format); +hidcardformat_t HIDGetCardFormat(int idx); +bool HIDPack(/* in */int FormatIndex, /* in */hidproxcard_t* card, /* out */hidproxmessage_t* packed); +bool HIDTryUnpack(/* in */hidproxmessage_t* packed, /* in */bool ignoreParity); + +#endif diff --git a/client/hidcardformatutils.c b/client/hidcardformatutils.c new file mode 100644 index 00000000..3abee223 --- /dev/null +++ b/client/hidcardformatutils.c @@ -0,0 +1,172 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// HID card format packing/unpacking support functions +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "hidcardformatutils.h" +#include "ui.h" + +bool get_bit_by_position(/* in */hidproxmessage_t* data, /* in */uint8_t pos){ + if (pos >= data->Length) return false; + pos = (data->Length - pos) - 1; // invert ordering; Indexing goes from 0 to 1. Subtract 1 for weight of bit. + bool result = false; + if (pos > 95) + result = false; + else if (pos > 63) + result = (data->top >> (pos - 64)) & 1; + else if (pos > 31) + result = (data->mid >> (pos - 32)) & 1; + else + result = (data->bot >> pos) & 1; + return result; +} +bool set_bit_by_position(/* inout */hidproxmessage_t* data, /* in */bool value, /* in */uint8_t pos){ + if (pos >= data->Length) return false; + pos = (data->Length - pos) - 1; // invert ordering; Indexing goes from 0 to 1. Subtract 1 for weight of bit. + if (pos > 95) { + return false; + } else if (pos > 63) { + if (value) + data->top |= (1 << (pos - 64)); + else + data->top &= ~(1 << (pos - 64)); + return true; + } else if (pos > 31) { + if (value) + data->mid |= (1 << (pos - 32)); + else + data->mid &= ~(1 << (pos - 32)); + return true; + } else { + if (value) + data->bot |= (1 << pos); + else + data->bot &= ~(1 << pos); + return true; + } +} +/** + * Safeguard the data by doing a manual deep copy + * + * At the time of the initial writing, the struct does not contain pointers. That doesn't + * mean it won't eventually contain one, however. To prevent memory leaks and erroneous + * aliasing, perform the copy function manually instead. Hence, this function. + * + * If the definition of the hidproxmessage struct changes, this function must also + * be updated to match. + */ +void proxmessage_datacopy(/*in*/hidproxmessage_t* src, /*out*/hidproxmessage_t* dest){ + dest->bot = src->bot; + dest->mid = src->mid; + dest->top = src->top; + dest->Length = src->Length; +} +/** + * + * Yes, this is horribly inefficient for linear data. + * The current code is a temporary measure to have a working function in place + * until all the bugs shaken from the block/chunk version of the code. + * + */ +uint64_t get_linear_field(/* in */hidproxmessage_t* data, uint8_t firstBit, uint8_t length){ + uint64_t result = 0; + for (uint8_t i = 0; i < length; i++ ) { + result = (result << 1) | get_bit_by_position(data, firstBit + i); + } + return result; +} +bool set_linear_field(/* inout */hidproxmessage_t* data, uint64_t value, uint8_t firstBit, uint8_t length){ + hidproxmessage_t tmpdata; + proxmessage_datacopy(data, &tmpdata); + bool result = true; + for (int i = 0; i < length; i++){ + result &= set_bit_by_position(&tmpdata, (value >> ((length - i) - 1)) & 1, firstBit + i); + } + if (result) proxmessage_datacopy(&tmpdata, data); + return result; +} + +uint64_t get_nonlinear_field(/* in */hidproxmessage_t* data, uint8_t numBits, uint8_t* bits){ + uint64_t result = 0; + for (int i = 0; i < numBits; i++){ + result = (result << 1) | (get_bit_by_position(data, *(bits+i)) & 1); + } + return result; +} +bool set_nonlinear_field(/* inout */hidproxmessage_t* data, uint64_t value, uint8_t numBits, uint8_t* bits){ + hidproxmessage_t tmpdata; + proxmessage_datacopy(data, &tmpdata); + bool result = true; + for (int i = 0; i < numBits; i++){ + result &= set_bit_by_position(&tmpdata, (value >> ((numBits - i) - 1)) & 1, *(bits + i)); + } + if (result) proxmessage_datacopy(&tmpdata, data); + return result; +} + +uint8_t get_length_from_header(/* inout */hidproxmessage_t* data) { + uint8_t len = 0; + + uint32_t hFmt; // for calculating card length + if ((data->top & 0x000FFFFF) > 0) { // > 64 bits + hFmt = data->top & 0x000FFFFF; + len = 64; + } else if ((data->mid & 0xFFFFFFC0) > 0) { // < 63-38 bits + hFmt = data->mid & 0xFFFFFFC0; + len = 32; + } else if ((data->mid & 0x00000020) == 0) { // 37 bits + hFmt = 0; + len = 37; + } else if ((data->mid & 0x0000001F) > 0){ // 36-32 bits + hFmt = data->mid & 0x0000001F; + len = 32; + } else { // <32 bits + hFmt = data->bot; + len = 0; + } + + while (hFmt > 1) { + hFmt >>= 1; + len++; + } + return len; +} + +hidproxmessage_t initialize_proxmessage_object(uint32_t top, uint32_t mid, uint32_t bot){ + struct hidproxmessage_s result; + memset(&result, 0, sizeof(hidproxmessage_t)); + result.top = top; + result.mid = mid; + result.bot = bot; + result.Length = get_length_from_header(&result); + return result; +} +bool add_HID_header(/* inout */hidproxmessage_t* data){ + if (data->Length > 84 || data->Length == 0) return false; // Invalid value + + if (data->Length >= 64){ + data->top |= 1 << (data->Length - 64); // leading 1: start bit + data->top |= 0x09e00000; // Extended-length header + } else if (data->Length > 37){ + data->mid |= 1 << (data->Length - 32); // leading 1: start bit + data->top |= 0x09e00000; // Extended-length header + } else if (data->Length == 37){ + // No header bits added to 37-bit cards + } else if (data->Length >= 32){ + data->mid |= 0x20; // Bit 37; standard header + data->mid |= 1 << (data->Length - 32); // leading 1: start bit + } else { + data->mid |= 0x20; // Bit 37; standard header + data->bot |= 1 << data->Length; // leading 1: start bit + } + return true; +} \ No newline at end of file diff --git a/client/hidcardformatutils.h b/client/hidcardformatutils.h new file mode 100644 index 00000000..0ab2ae72 --- /dev/null +++ b/client/hidcardformatutils.h @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 grauerfuchs +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// HID card format packing/unpacking support functions +//----------------------------------------------------------------------------- + +#ifndef HIDCARDFORMATUTILS_H__ +#define HIDCARDFORMATUTILS_H__ + +#include +#include +#include + +// Structure for packed HID messages +// Always align lowest value (last transmitted) bit to ordinal position 0 (lowest valued bit bottom) +typedef struct hidproxmessage_s{ + uint8_t Length; // Number of encoded bits in prox message (excluding headers and preamble) + uint32_t top; // Bits in x<<64 positions + uint32_t mid; // Bits in x<<32 positions + uint32_t bot; // Lowest ordinal positions +} hidproxmessage_t; + +// Structure for unpacked HID prox cards +typedef struct hidproxcard_s{ + uint32_t FacilityCode; + uint64_t CardNumber; + uint32_t IssueLevel; + uint32_t OEM; + bool ParityValid; // Only valid for responses +} hidproxcard_t; + +bool get_bit_by_position(/* in */hidproxmessage_t* data, /* in */uint8_t pos); +bool set_bit_by_position(/* inout */hidproxmessage_t* data, /* in */bool value, /* in */uint8_t pos); + +uint64_t get_linear_field(/* in */hidproxmessage_t* data, /* in */uint8_t firstBit, /* in */uint8_t length); +bool set_linear_field(/* inout */hidproxmessage_t* data, /* in */uint64_t value, /* in */uint8_t firstBit, /* in */uint8_t length); + +uint64_t get_nonlinear_field(/* in */hidproxmessage_t* data, /* in */uint8_t numBits, /* in */uint8_t* bits); +bool set_nonlinear_field(/* inout */hidproxmessage_t* data, /* in */uint64_t value, /* in */uint8_t numBits, /* in */uint8_t* bits); + +hidproxmessage_t initialize_proxmessage_object(/* in */uint32_t top, /* in */uint32_t mid, /* in */uint32_t bot); +bool add_HID_header(/* inout */hidproxmessage_t* data); +#endif diff --git a/client/jansson/Makefile b/client/jansson/Makefile new file mode 100644 index 00000000..e608d7c0 --- /dev/null +++ b/client/jansson/Makefile @@ -0,0 +1,64 @@ + +include_HEADERS = jansson.h +nodist_include_HEADERS = jansson_config.h + +LIB_A = libjansson.a +libjansson_la_SOURCES = \ + dump.c \ + error.c \ + hashtable.c \ + hashtable.h \ + hashtable_seed.c \ + jansson_private.h \ + load.c \ + lookup3.h \ + memory.c \ + pack_unpack.c \ + strbuffer.c \ + strbuffer.h \ + strconv.c \ + utf.c \ + utf.h \ + path.c \ + value.c +libjansson_la_LDFLAGS = \ + -no-undefined \ + -export-symbols-regex '^json_' \ + -version-info 15:0:11 + + +CFILES = $(filter %.c, $(libjansson_la_SOURCES)) +CMDOBJS = $(CFILES:%.c=%.o) +CLEAN = $(CMDOBJS) + +CC= gcc +CFLAGS= -O2 -Wall -Wno-unused-variable -Wno-unused-function +LDFLAGS= $(SYSLDFLAGS) $(libjansson_la_LDFLAGS) +LIBS= -lm $(SYSLIBS) $(MYLIBS) +DEFAULT_INCLUDES = -I. +DEFS = -DHAVE_STDINT_H + +AR= ar rcs +RANLIB= ranlib +RM= rm -f +TST= echo + +SYSLDFLAGS= +SYSLIBS= + +MYLIBS= +MYOBJS= + +all: $(CMDOBJS) + $(AR) $(LIB_A) $(CMDOBJS) + $(RANLIB) $(LIB_A) + +clean: + $(RM) $(CLEAN) + $(RM) $(LIB_A) + +%.o: %.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(CFLAGS) -c -o $@ $< $(LIBS) + +.PHONY: all clean + diff --git a/client/jansson/dump.c b/client/jansson/dump.c new file mode 100644 index 00000000..8e725c93 --- /dev/null +++ b/client/jansson/dump.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "jansson_private.h" + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "jansson.h" +#include "strbuffer.h" +#include "utf.h" + +#define MAX_INTEGER_STR_LENGTH 100 +#define MAX_REAL_STR_LENGTH 100 + +#define FLAGS_TO_INDENT(f) ((f) & 0x1F) +#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F) + +struct buffer { + const size_t size; + size_t used; + char *data; +}; + +static int dump_to_strbuffer(const char *buffer, size_t size, void *data) +{ + return strbuffer_append_bytes((strbuffer_t *)data, buffer, size); +} + +static int dump_to_buffer(const char *buffer, size_t size, void *data) +{ + struct buffer *buf = (struct buffer *)data; + + if(buf->used + size <= buf->size) + memcpy(&buf->data[buf->used], buffer, size); + + buf->used += size; + return 0; +} + +static int dump_to_file(const char *buffer, size_t size, void *data) +{ + FILE *dest = (FILE *)data; + if(fwrite(buffer, size, 1, dest) != 1) + return -1; + return 0; +} + +static int dump_to_fd(const char *buffer, size_t size, void *data) +{ + int *dest = (int *)data; +#ifdef HAVE_UNISTD_H + if(write(*dest, buffer, size) == (ssize_t)size) + return 0; +#endif + return -1; +} + +/* 32 spaces (the maximum indentation size) */ +static const char whitespace[] = " "; + +static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) +{ + if(FLAGS_TO_INDENT(flags) > 0) + { + unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count; + + if(dump("\n", 1, data)) + return -1; + + while(n_spaces > 0) + { + int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1; + + if(dump(whitespace, cur_n, data)) + return -1; + + n_spaces -= cur_n; + } + } + else if(space && !(flags & JSON_COMPACT)) + { + return dump(" ", 1, data); + } + return 0; +} + +static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags) +{ + const char *pos, *end, *lim; + int32_t codepoint; + + if(dump("\"", 1, data)) + return -1; + + end = pos = str; + lim = str + len; + while(1) + { + const char *text; + char seq[13]; + int length; + + while(end < lim) + { + end = utf8_iterate(pos, lim - pos, &codepoint); + if(!end) + return -1; + + /* mandatory escape or control char */ + if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20) + break; + + /* slash */ + if((flags & JSON_ESCAPE_SLASH) && codepoint == '/') + break; + + /* non-ASCII */ + if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F) + break; + + pos = end; + } + + if(pos != str) { + if(dump(str, pos - str, data)) + return -1; + } + + if(end == pos) + break; + + /* handle \, /, ", and control codes */ + length = 2; + switch(codepoint) + { + case '\\': text = "\\\\"; break; + case '\"': text = "\\\""; break; + case '\b': text = "\\b"; break; + case '\f': text = "\\f"; break; + case '\n': text = "\\n"; break; + case '\r': text = "\\r"; break; + case '\t': text = "\\t"; break; + case '/': text = "\\/"; break; + default: + { + /* codepoint is in BMP */ + if(codepoint < 0x10000) + { + snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint); + length = 6; + } + + /* not in BMP -> construct a UTF-16 surrogate pair */ + else + { + int32_t first, last; + + codepoint -= 0x10000; + first = 0xD800 | ((codepoint & 0xffc00) >> 10); + last = 0xDC00 | (codepoint & 0x003ff); + + snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last); + length = 12; + } + + text = seq; + break; + } + } + + if(dump(text, length, data)) + return -1; + + str = pos = end; + } + + return dump("\"", 1, data); +} + +static int compare_keys(const void *key1, const void *key2) +{ + return strcmp(*(const char **)key1, *(const char **)key2); +} + +static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size) +{ + snprintf(key, key_size, "%p", json); + if (hashtable_get(parents, key)) + return -1; + + return hashtable_set(parents, key, json_null()); +} + +static int do_dump(const json_t *json, size_t flags, int depth, + hashtable_t *parents, json_dump_callback_t dump, void *data) +{ + int embed = flags & JSON_EMBED; + + flags &= ~JSON_EMBED; + + if(!json) + return -1; + + switch(json_typeof(json)) { + case JSON_NULL: + return dump("null", 4, data); + + case JSON_TRUE: + return dump("true", 4, data); + + case JSON_FALSE: + return dump("false", 5, data); + + case JSON_INTEGER: + { + char buffer[MAX_INTEGER_STR_LENGTH]; + int size; + + size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, + "%" JSON_INTEGER_FORMAT, + json_integer_value(json)); + if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) + return -1; + + return dump(buffer, size, data); + } + + case JSON_REAL: + { + char buffer[MAX_REAL_STR_LENGTH]; + int size; + double value = json_real_value(json); + + size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value, + FLAGS_TO_PRECISION(flags)); + if(size < 0) + return -1; + + return dump(buffer, size, data); + } + + case JSON_STRING: + return dump_string(json_string_value(json), json_string_length(json), dump, data, flags); + + case JSON_ARRAY: + { + size_t n; + size_t i; + /* Space for "0x", double the sizeof a pointer for the hex and a terminator. */ + char key[2 + (sizeof(json) * 2) + 1]; + + /* detect circular references */ + if (loop_check(parents, json, key, sizeof(key))) + return -1; + + n = json_array_size(json); + + if(!embed && dump("[", 1, data)) + return -1; + if(n == 0) { + hashtable_del(parents, key); + return embed ? 0 : dump("]", 1, data); + } + if(dump_indent(flags, depth + 1, 0, dump, data)) + return -1; + + for(i = 0; i < n; ++i) { + if(do_dump(json_array_get(json, i), flags, depth + 1, + parents, dump, data)) + return -1; + + if(i < n - 1) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + return -1; + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + return -1; + } + } + + hashtable_del(parents, key); + return embed ? 0 : dump("]", 1, data); + } + + case JSON_OBJECT: + { + void *iter; + const char *separator; + int separator_length; + /* Space for "0x", double the sizeof a pointer for the hex and a terminator. */ + char key[2 + (sizeof(json) * 2) + 1]; + + if(flags & JSON_COMPACT) { + separator = ":"; + separator_length = 1; + } + else { + separator = ": "; + separator_length = 2; + } + + /* detect circular references */ + if (loop_check(parents, json, key, sizeof(key))) + return -1; + + iter = json_object_iter((json_t *)json); + + if(!embed && dump("{", 1, data)) + return -1; + if(!iter) { + hashtable_del(parents, key); + return embed ? 0 : dump("}", 1, data); + } + if(dump_indent(flags, depth + 1, 0, dump, data)) + return -1; + + if(flags & JSON_SORT_KEYS) + { + const char **keys; + size_t size, i; + + size = json_object_size(json); + keys = jsonp_malloc(size * sizeof(const char *)); + if(!keys) + return -1; + + i = 0; + while(iter) + { + keys[i] = json_object_iter_key(iter); + iter = json_object_iter_next((json_t *)json, iter); + i++; + } + assert(i == size); + + qsort(keys, size, sizeof(const char *), compare_keys); + + for(i = 0; i < size; i++) + { + const char *key; + json_t *value; + + key = keys[i]; + value = json_object_get(json, key); + assert(value); + + dump_string(key, strlen(key), dump, data, flags); + if(dump(separator, separator_length, data) || + do_dump(value, flags, depth + 1, parents, dump, data)) + { + jsonp_free(keys); + return -1; + } + + if(i < size - 1) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + { + jsonp_free(keys); + return -1; + } + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + { + jsonp_free(keys); + return -1; + } + } + } + + jsonp_free(keys); + } + else + { + /* Don't sort keys */ + + while(iter) + { + void *next = json_object_iter_next((json_t *)json, iter); + const char *key = json_object_iter_key(iter); + + dump_string(key, strlen(key), dump, data, flags); + if(dump(separator, separator_length, data) || + do_dump(json_object_iter_value(iter), flags, depth + 1, + parents, dump, data)) + return -1; + + if(next) + { + if(dump(",", 1, data) || + dump_indent(flags, depth + 1, 1, dump, data)) + return -1; + } + else + { + if(dump_indent(flags, depth, 0, dump, data)) + return -1; + } + + iter = next; + } + } + + hashtable_del(parents, key); + return embed ? 0 : dump("}", 1, data); + } + + default: + /* not reached */ + return -1; + } +} + +char *json_dumps(const json_t *json, size_t flags) +{ + strbuffer_t strbuff; + char *result; + + if(strbuffer_init(&strbuff)) + return NULL; + + if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags)) + result = NULL; + else + result = jsonp_strdup(strbuffer_value(&strbuff)); + + strbuffer_close(&strbuff); + return result; +} + +size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) +{ + struct buffer buf = { size, 0, buffer }; + + if(json_dump_callback(json, dump_to_buffer, (void *)&buf, flags)) + return 0; + + return buf.used; +} + +int json_dumpf(const json_t *json, FILE *output, size_t flags) +{ + return json_dump_callback(json, dump_to_file, (void *)output, flags); +} + +int json_dumpfd(const json_t *json, int output, size_t flags) +{ + return json_dump_callback(json, dump_to_fd, (void *)&output, flags); +} + +int json_dump_file(const json_t *json, const char *path, size_t flags) +{ + int result; + + FILE *output = fopen(path, "w"); + if(!output) + 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)) + return -1; + res = do_dump(json, flags, 0, &parents_set, callback, data); + hashtable_close(&parents_set); + + return res; +} diff --git a/client/jansson/error.c b/client/jansson/error.c new file mode 100644 index 00000000..b94b3a3a --- /dev/null +++ b/client/jansson/error.c @@ -0,0 +1,66 @@ +#include +#include "jansson_private.h" + +void jsonp_error_init(json_error_t *error, const char *source) +{ + if(error) + { + error->text[0] = '\0'; + error->line = -1; + error->column = -1; + error->position = 0; + if(source) + jsonp_error_set_source(error, source); + else + error->source[0] = '\0'; + } +} + +void jsonp_error_set_source(json_error_t *error, const char *source) +{ + size_t length; + + if(!error || !source) + return; + + length = strlen(source); + if(length < JSON_ERROR_SOURCE_LENGTH) + strncpy(error->source, source, JSON_ERROR_SOURCE_LENGTH); + else { + size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4; + memcpy(error->source, "...", 3); + strncpy(error->source + 3, source + extra, length - extra + 1); + } +} + +void jsonp_error_set(json_error_t *error, int line, int column, + size_t position, enum json_error_code code, + const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + jsonp_error_vset(error, line, column, position, code, msg, ap); + va_end(ap); +} + +void jsonp_error_vset(json_error_t *error, int line, int column, + size_t position, enum json_error_code code, + const char *msg, va_list ap) +{ + if(!error) + return; + + if(error->text[0] != '\0') { + /* error already set */ + return; + } + + error->line = line; + error->column = column; + error->position = (int)position; + + vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH - 1, msg, ap); + error->text[JSON_ERROR_TEXT_LENGTH - 2] = '\0'; + error->text[JSON_ERROR_TEXT_LENGTH - 1] = code; +} diff --git a/client/jansson/hashtable.c b/client/jansson/hashtable.c new file mode 100644 index 00000000..c819319a --- /dev/null +++ b/client/jansson/hashtable.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include + +#if HAVE_STDINT_H +#include +#endif + +#include /* for JSON_INLINE */ +#include "jansson_private.h" /* for container_of() */ +#include "hashtable.h" + +#ifndef INITIAL_HASHTABLE_ORDER +#define INITIAL_HASHTABLE_ORDER 3 +#endif + +typedef struct hashtable_list list_t; +typedef struct hashtable_pair pair_t; +typedef struct hashtable_bucket bucket_t; + +extern volatile uint32_t hashtable_seed; + +/* Implementation of the hash function */ +#include "lookup3.h" + +#define list_to_pair(list_) container_of(list_, pair_t, list) +#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list) +#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed)) + +static JSON_INLINE void list_init(list_t *list) +{ + list->next = list; + list->prev = list; +} + +static JSON_INLINE void list_insert(list_t *list, list_t *node) +{ + node->next = list; + node->prev = list->prev; + list->prev->next = node; + list->prev = node; +} + +static JSON_INLINE void list_remove(list_t *list) +{ + list->prev->next = list->next; + list->next->prev = list->prev; +} + +static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket) +{ + return bucket->first == &hashtable->list && bucket->first == bucket->last; +} + +static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, + list_t *list) +{ + if(bucket_is_empty(hashtable, bucket)) + { + list_insert(&hashtable->list, list); + bucket->first = bucket->last = list; + } + else + { + list_insert(bucket->first, list); + bucket->first = list; + } +} + +static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket, + const char *key, size_t hash) +{ + list_t *list; + pair_t *pair; + + if(bucket_is_empty(hashtable, bucket)) + return NULL; + + list = bucket->first; + while(1) + { + pair = list_to_pair(list); + if(pair->hash == hash && strcmp(pair->key, key) == 0) + return pair; + + if(list == bucket->last) + break; + + list = list->next; + } + + return NULL; +} + +/* returns 0 on success, -1 if key was not found */ +static int hashtable_do_del(hashtable_t *hashtable, + const char *key, size_t hash) +{ + pair_t *pair; + bucket_t *bucket; + size_t index; + + index = hash & hashmask(hashtable->order); + bucket = &hashtable->buckets[index]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return -1; + + if(&pair->list == bucket->first && &pair->list == bucket->last) + bucket->first = bucket->last = &hashtable->list; + + else if(&pair->list == bucket->first) + bucket->first = pair->list.next; + + else if(&pair->list == bucket->last) + bucket->last = pair->list.prev; + + list_remove(&pair->list); + list_remove(&pair->ordered_list); + json_decref(pair->value); + + jsonp_free(pair); + hashtable->size--; + + return 0; +} + +static void hashtable_do_clear(hashtable_t *hashtable) +{ + list_t *list, *next; + pair_t *pair; + + for(list = hashtable->list.next; list != &hashtable->list; list = next) + { + next = list->next; + pair = list_to_pair(list); + json_decref(pair->value); + jsonp_free(pair); + } +} + +static int hashtable_do_rehash(hashtable_t *hashtable) +{ + list_t *list, *next; + pair_t *pair; + size_t i, index, new_size, new_order; + struct hashtable_bucket *new_buckets; + + new_order = hashtable->order + 1; + new_size = hashsize(new_order); + + new_buckets = jsonp_malloc(new_size * sizeof(bucket_t)); + if(!new_buckets) + return -1; + + jsonp_free(hashtable->buckets); + hashtable->buckets = new_buckets; + hashtable->order = new_order; + + for(i = 0; i < hashsize(hashtable->order); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + list = hashtable->list.next; + list_init(&hashtable->list); + + for(; list != &hashtable->list; list = next) { + next = list->next; + pair = list_to_pair(list); + index = pair->hash % new_size; + insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list); + } + + return 0; +} + + +int hashtable_init(hashtable_t *hashtable) +{ + size_t i; + + hashtable->size = 0; + hashtable->order = INITIAL_HASHTABLE_ORDER; + hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t)); + if(!hashtable->buckets) + return -1; + + list_init(&hashtable->list); + list_init(&hashtable->ordered_list); + + for(i = 0; i < hashsize(hashtable->order); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + return 0; +} + +void hashtable_close(hashtable_t *hashtable) +{ + hashtable_do_clear(hashtable); + jsonp_free(hashtable->buckets); +} + +int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value) +{ + pair_t *pair; + bucket_t *bucket; + size_t hash, index; + + /* rehash if the load ratio exceeds 1 */ + if(hashtable->size >= hashsize(hashtable->order)) + if(hashtable_do_rehash(hashtable)) + return -1; + + hash = hash_str(key); + index = hash & hashmask(hashtable->order); + bucket = &hashtable->buckets[index]; + pair = hashtable_find_pair(hashtable, bucket, key, hash); + + if(pair) + { + json_decref(pair->value); + pair->value = value; + } + else + { + /* offsetof(...) returns the size of pair_t without the last, + flexible member. This way, the correct amount is + allocated. */ + + size_t len = strlen(key); + if(len >= (size_t)-1 - offsetof(pair_t, key)) { + /* Avoid an overflow if the key is very long */ + return -1; + } + + pair = jsonp_malloc(offsetof(pair_t, key) + len + 1); + if(!pair) + return -1; + + pair->hash = hash; + strncpy(pair->key, key, len + 1); + pair->value = value; + list_init(&pair->list); + list_init(&pair->ordered_list); + + insert_to_bucket(hashtable, bucket, &pair->list); + list_insert(&hashtable->ordered_list, &pair->ordered_list); + + hashtable->size++; + } + return 0; +} + +void *hashtable_get(hashtable_t *hashtable, const char *key) +{ + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key); + bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return NULL; + + return pair->value; +} + +int hashtable_del(hashtable_t *hashtable, const char *key) +{ + size_t hash = hash_str(key); + return hashtable_do_del(hashtable, key, hash); +} + +void hashtable_clear(hashtable_t *hashtable) +{ + size_t i; + + hashtable_do_clear(hashtable); + + for(i = 0; i < hashsize(hashtable->order); i++) + { + hashtable->buckets[i].first = hashtable->buckets[i].last = + &hashtable->list; + } + + list_init(&hashtable->list); + list_init(&hashtable->ordered_list); + hashtable->size = 0; +} + +void *hashtable_iter(hashtable_t *hashtable) +{ + return hashtable_iter_next(hashtable, &hashtable->ordered_list); +} + +void *hashtable_iter_at(hashtable_t *hashtable, const char *key) +{ + pair_t *pair; + size_t hash; + bucket_t *bucket; + + hash = hash_str(key); + bucket = &hashtable->buckets[hash & hashmask(hashtable->order)]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return NULL; + + return &pair->ordered_list; +} + +void *hashtable_iter_next(hashtable_t *hashtable, void *iter) +{ + list_t *list = (list_t *)iter; + if(list->next == &hashtable->ordered_list) + return NULL; + return list->next; +} + +void *hashtable_iter_key(void *iter) +{ + pair_t *pair = ordered_list_to_pair((list_t *)iter); + return pair->key; +} + +void *hashtable_iter_value(void *iter) +{ + pair_t *pair = ordered_list_to_pair((list_t *)iter); + return pair->value; +} + +void hashtable_iter_set(void *iter, json_t *value) +{ + pair_t *pair = ordered_list_to_pair((list_t *)iter); + + json_decref(pair->value); + pair->value = value; +} diff --git a/client/jansson/hashtable.h b/client/jansson/hashtable.h new file mode 100644 index 00000000..d4c32ae0 --- /dev/null +++ b/client/jansson/hashtable.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef HASHTABLE_H +#define HASHTABLE_H + +#include +#include "jansson.h" + +struct hashtable_list { + struct hashtable_list *prev; + struct hashtable_list *next; +}; + +/* "pair" may be a bit confusing a name, but think of it as a + key-value pair. In this case, it just encodes some extra data, + too */ +struct hashtable_pair { + struct hashtable_list list; + struct hashtable_list ordered_list; + size_t hash; + json_t *value; + char key[1]; +}; + +struct hashtable_bucket { + struct hashtable_list *first; + struct hashtable_list *last; +}; + +typedef struct hashtable { + size_t size; + struct hashtable_bucket *buckets; + size_t order; /* hashtable has pow(2, order) buckets */ + struct hashtable_list list; + struct hashtable_list ordered_list; +} hashtable_t; + + +#define hashtable_key_to_iter(key_) \ + (&(container_of(key_, struct hashtable_pair, key)->ordered_list)) + + +/** + * hashtable_init - Initialize a hashtable object + * + * @hashtable: The (statically allocated) hashtable object + * + * Initializes a statically allocated hashtable object. The object + * should be cleared with hashtable_close when it's no longer used. + * + * Returns 0 on success, -1 on error (out of memory). + */ +int hashtable_init(hashtable_t *hashtable); + +/** + * hashtable_close - Release all resources used by a hashtable object + * + * @hashtable: The hashtable + * + * Destroys a statically allocated hashtable object. + */ +void hashtable_close(hashtable_t *hashtable); + +/** + * hashtable_set - Add/modify value in hashtable + * + * @hashtable: The hashtable object + * @key: The key + * @serial: For addition order of keys + * @value: The value + * + * If a value with the given key already exists, its value is replaced + * with the new value. Value is "stealed" in the sense that hashtable + * doesn't increment its refcount but decreases the refcount when the + * value is no longer needed. + * + * Returns 0 on success, -1 on failure (out of memory). + */ +int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value); + +/** + * hashtable_get - Get a value associated with a key + * + * @hashtable: The hashtable object + * @key: The key + * + * Returns value if it is found, or NULL otherwise. + */ +void *hashtable_get(hashtable_t *hashtable, const char *key); + +/** + * hashtable_del - Remove a value from the hashtable + * + * @hashtable: The hashtable object + * @key: The key + * + * Returns 0 on success, or -1 if the key was not found. + */ +int hashtable_del(hashtable_t *hashtable, const char *key); + +/** + * hashtable_clear - Clear hashtable + * + * @hashtable: The hashtable object + * + * Removes all items from the hashtable. + */ +void hashtable_clear(hashtable_t *hashtable); + +/** + * hashtable_iter - Iterate over hashtable + * + * @hashtable: The hashtable object + * + * Returns an opaque iterator to the first element in the hashtable. + * The iterator should be passed to hashtable_iter_* functions. + * The hashtable items are not iterated over in any particular order. + * + * There's no need to free the iterator in any way. The iterator is + * valid as long as the item that is referenced by the iterator is not + * deleted. Other values may be added or deleted. In particular, + * hashtable_iter_next() may be called on an iterator, and after that + * the key/value pair pointed by the old iterator may be deleted. + */ +void *hashtable_iter(hashtable_t *hashtable); + +/** + * hashtable_iter_at - Return an iterator at a specific key + * + * @hashtable: The hashtable object + * @key: The key that the iterator should point to + * + * Like hashtable_iter() but returns an iterator pointing to a + * specific key. + */ +void *hashtable_iter_at(hashtable_t *hashtable, const char *key); + +/** + * hashtable_iter_next - Advance an iterator + * + * @hashtable: The hashtable object + * @iter: The iterator + * + * Returns a new iterator pointing to the next element in the + * hashtable or NULL if the whole hastable has been iterated over. + */ +void *hashtable_iter_next(hashtable_t *hashtable, void *iter); + +/** + * hashtable_iter_key - Retrieve the key pointed by an iterator + * + * @iter: The iterator + */ +void *hashtable_iter_key(void *iter); + +/** + * hashtable_iter_value - Retrieve the value pointed by an iterator + * + * @iter: The iterator + */ +void *hashtable_iter_value(void *iter); + +/** + * hashtable_iter_set - Set the value pointed by an iterator + * + * @iter: The iterator + * @value: The value to set + */ +void hashtable_iter_set(void *iter, json_t *value); + +#endif diff --git a/client/jansson/hashtable_seed.c b/client/jansson/hashtable_seed.c new file mode 100644 index 00000000..8aed5406 --- /dev/null +++ b/client/jansson/hashtable_seed.c @@ -0,0 +1,277 @@ +/* Generate sizeof(uint32_t) bytes of as random data as possible to seed + the hash function. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#ifdef HAVE_STDINT_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SCHED_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#if defined(_WIN32) +/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */ +#include +#endif + +#include "jansson.h" + + +static uint32_t buf_to_uint32(char *data) { + size_t i; + uint32_t result = 0; + + for (i = 0; i < sizeof(uint32_t); i++) + result = (result << 8) | (unsigned char)data[i]; + + return result; +} + + + +/* /dev/urandom */ +#if !defined(_WIN32) && defined(USE_URANDOM) +static int seed_from_urandom(uint32_t *seed) { + /* Use unbuffered I/O if we have open(), close() and read(). Otherwise + fall back to fopen() */ + + char data[sizeof(uint32_t)]; + int ok; + +#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ) + int urandom; + urandom = open("/dev/urandom", O_RDONLY); + if (urandom == -1) + return 1; + + ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t); + close(urandom); +#else + FILE *urandom; + + urandom = fopen("/dev/urandom", "rb"); + if (!urandom) + return 1; + + ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t); + fclose(urandom); +#endif + + if (!ok) + return 1; + + *seed = buf_to_uint32(data); + return 0; +} +#endif + +/* Windows Crypto API */ +#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) +#include + +typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags); +typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer); +typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags); + +static int seed_from_windows_cryptoapi(uint32_t *seed) +{ + HINSTANCE hAdvAPI32 = NULL; + CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL; + CRYPTGENRANDOM pCryptGenRandom = NULL; + CRYPTRELEASECONTEXT pCryptReleaseContext = NULL; + HCRYPTPROV hCryptProv = 0; + BYTE data[sizeof(uint32_t)]; + int ok; + + hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll")); + if(hAdvAPI32 == NULL) + return 1; + + pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA"); + if (!pCryptAcquireContext) + return 1; + + pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom"); + if (!pCryptGenRandom) + return 1; + + pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext"); + if (!pCryptReleaseContext) + return 1; + + if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + return 1; + + ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data); + pCryptReleaseContext(hCryptProv, 0); + + if (!ok) + return 1; + + *seed = buf_to_uint32((char *)data); + return 0; +} +#endif + +/* gettimeofday() and getpid() */ +static int seed_from_timestamp_and_pid(uint32_t *seed) { +#ifdef HAVE_GETTIMEOFDAY + /* XOR of seconds and microseconds */ + struct timeval tv; + gettimeofday(&tv, NULL); + *seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec; +#else + /* Seconds only */ + *seed = (uint32_t)time(NULL); +#endif + + /* XOR with PID for more randomness */ +#if defined(_WIN32) + *seed ^= (uint32_t)GetCurrentProcessId(); +#elif defined(HAVE_GETPID) + *seed ^= (uint32_t)getpid(); +#endif + + return 0; +} + +static uint32_t generate_seed() { + uint32_t seed; + int done = 0; + +#if !defined(_WIN32) && defined(USE_URANDOM) + if (seed_from_urandom(&seed) == 0) + done = 1; +#endif + +#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) + if (seed_from_windows_cryptoapi(&seed) == 0) + done = 1; +#endif + + if (!done) { + /* Fall back to timestamp and PID if no better randomness is + available */ + seed_from_timestamp_and_pid(&seed); + } + + /* Make sure the seed is never zero */ + if (seed == 0) + seed = 1; + + return seed; +} + + +volatile uint32_t hashtable_seed = 0; + +#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) +static volatile char seed_initialized = 0; + +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) { + /* Do the seeding ourselves */ + if (new_seed == 0) + new_seed = generate_seed(); + + __atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE); + } else { + /* Wait for another thread to do the seeding */ + do { +#ifdef HAVE_SCHED_YIELD + sched_yield(); +#endif + } while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0); + } + } +} +#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (new_seed == 0) { + /* Explicit synchronization fences are not supported by the + __sync builtins, so every thread getting here has to + generate the seed value. + */ + new_seed = generate_seed(); + } + + do { + if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) { + /* We were the first to seed */ + break; + } else { + /* Wait for another thread to do the seeding */ +#ifdef HAVE_SCHED_YIELD + sched_yield(); +#endif + } + } while(hashtable_seed == 0); + } +} +#elif defined(_WIN32) +static long seed_initialized = 0; +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (InterlockedIncrement(&seed_initialized) == 1) { + /* Do the seeding ourselves */ + if (new_seed == 0) + new_seed = generate_seed(); + + hashtable_seed = new_seed; + } else { + /* Wait for another thread to do the seeding */ + do { + SwitchToThread(); + } while (hashtable_seed == 0); + } + } +} +#else +/* Fall back to a thread-unsafe version */ +void json_object_seed(size_t seed) { + uint32_t new_seed = (uint32_t)seed; + + if (hashtable_seed == 0) { + if (new_seed == 0) + new_seed = generate_seed(); + + hashtable_seed = new_seed; + } +} +#endif diff --git a/client/jansson/jansson.def b/client/jansson/jansson.def new file mode 100644 index 00000000..15f35c95 --- /dev/null +++ b/client/jansson/jansson.def @@ -0,0 +1,75 @@ +EXPORTS + json_delete + json_true + json_false + json_null + json_sprintf + json_vsprintf + json_string + json_stringn + json_string_nocheck + json_stringn_nocheck + json_string_value + json_string_length + json_string_set + json_string_setn + json_string_set_nocheck + json_string_setn_nocheck + json_integer + json_integer_value + json_integer_set + json_real + json_real_value + json_real_set + json_number_value + json_array + json_array_size + json_array_get + json_array_set_new + json_array_append_new + json_array_insert_new + json_array_remove + json_array_clear + json_array_extend + json_object + json_object_size + json_object_get + json_object_set_new + json_object_set_new_nocheck + json_object_del + json_object_clear + json_object_update + json_object_update_existing + json_object_update_missing + json_object_iter + json_object_iter_at + json_object_iter_next + json_object_iter_key + json_object_iter_value + json_object_iter_set_new + json_object_key_to_iter + json_object_seed + json_dumps + json_dumpb + json_dumpf + json_dumpfd + json_dump_file + json_dump_callback + json_loads + json_loadb + json_loadf + json_loadfd + json_load_file + json_load_callback + json_equal + json_copy + json_deep_copy + json_pack + json_pack_ex + json_vpack_ex + json_unpack + json_unpack_ex + json_vunpack_ex + json_set_alloc_funcs + json_get_alloc_funcs + diff --git a/client/jansson/jansson.h b/client/jansson/jansson.h new file mode 100644 index 00000000..86f23d17 --- /dev/null +++ b/client/jansson/jansson.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_H +#define JANSSON_H + +#include +#include +#include /* for size_t */ +#include + +#include "jansson_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ + +#define JANSSON_MAJOR_VERSION 2 +#define JANSSON_MINOR_VERSION 11 +#define JANSSON_MICRO_VERSION 0 + +/* Micro version is omitted if it's 0 */ +#define JANSSON_VERSION "2.11" + +/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this + for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ +#define JANSSON_VERSION_HEX ((JANSSON_MAJOR_VERSION << 16) | \ + (JANSSON_MINOR_VERSION << 8) | \ + (JANSSON_MICRO_VERSION << 0)) + +/* If __atomic or __sync builtins are available the library is thread + * safe for all read-only functions plus reference counting. */ +#if JSON_HAVE_ATOMIC_BUILTINS || JSON_HAVE_SYNC_BUILTINS +#define JANSSON_THREAD_SAFE_REFCOUNT 1 +#endif + +/* types */ + +typedef enum { + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_INTEGER, + JSON_REAL, + JSON_TRUE, + JSON_FALSE, + JSON_NULL +} json_type; + +typedef struct json_t { + json_type type; + volatile size_t refcount; +} json_t; + +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _WIN32 +#define JSON_INTEGER_FORMAT "I64d" +#else +#define JSON_INTEGER_FORMAT "lld" +#endif +typedef long long json_int_t; +#else +#define JSON_INTEGER_FORMAT "ld" +typedef long json_int_t; +#endif /* JSON_INTEGER_IS_LONG_LONG */ +#endif + +#define json_typeof(json) ((json)->type) +#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT) +#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY) +#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING) +#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER) +#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL) +#define json_is_number(json) (json_is_integer(json) || json_is_real(json)) +#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE) +#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE) +#define json_boolean_value json_is_true +#define json_is_boolean(json) (json_is_true(json) || json_is_false(json)) +#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL) + +/* construction, destruction, reference counting */ + +json_t *json_object(void); +json_t *json_array(void); +json_t *json_string(const char *value); +json_t *json_stringn(const char *value, size_t len); +json_t *json_string_nocheck(const char *value); +json_t *json_stringn_nocheck(const char *value, size_t len); +json_t *json_integer(json_int_t value); +json_t *json_real(double value); +json_t *json_true(void); +json_t *json_false(void); +#define json_boolean(val) ((val) ? json_true() : json_false()) +json_t *json_null(void); + +/* do not call JSON_INTERNAL_INCREF or JSON_INTERNAL_DECREF directly */ +#if JSON_HAVE_ATOMIC_BUILTINS +#define JSON_INTERNAL_INCREF(json) __atomic_add_fetch(&json->refcount, 1, __ATOMIC_ACQUIRE) +#define JSON_INTERNAL_DECREF(json) __atomic_sub_fetch(&json->refcount, 1, __ATOMIC_RELEASE) +#elif JSON_HAVE_SYNC_BUILTINS +#define JSON_INTERNAL_INCREF(json) __sync_add_and_fetch(&json->refcount, 1) +#define JSON_INTERNAL_DECREF(json) __sync_sub_and_fetch(&json->refcount, 1) +#else +#define JSON_INTERNAL_INCREF(json) (++json->refcount) +#define JSON_INTERNAL_DECREF(json) (--json->refcount) +#endif + +static JSON_INLINE +json_t *json_incref(json_t *json) +{ + if(json && json->refcount != (size_t)-1) + JSON_INTERNAL_INCREF(json); + return json; +} + +/* do not call json_delete directly */ +void json_delete(json_t *json); + +static JSON_INLINE +void json_decref(json_t *json) +{ + if(json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0) + json_delete(json); +} + +#if defined(__GNUC__) || defined(__clang__) +static JSON_INLINE +void json_decrefp(json_t **json) +{ + if(json) { + json_decref(*json); + *json = NULL; + } +} + +#define json_auto_t json_t __attribute__((cleanup(json_decrefp))) +#endif + + +/* error reporting */ + +#define JSON_ERROR_TEXT_LENGTH 160 +#define JSON_ERROR_SOURCE_LENGTH 80 + +typedef struct json_error_t { + int line; + int column; + int position; + char source[JSON_ERROR_SOURCE_LENGTH]; + char text[JSON_ERROR_TEXT_LENGTH]; +} json_error_t; + +enum json_error_code { + json_error_unknown, + json_error_out_of_memory, + json_error_stack_overflow, + json_error_cannot_open_file, + json_error_invalid_argument, + json_error_invalid_utf8, + json_error_premature_end_of_input, + json_error_end_of_input_expected, + json_error_invalid_syntax, + json_error_invalid_format, + json_error_wrong_type, + json_error_null_character, + json_error_null_value, + json_error_null_byte_in_key, + json_error_duplicate_key, + json_error_numeric_overflow, + json_error_item_not_found, + json_error_index_out_of_range +}; + +static JSON_INLINE enum json_error_code json_error_code(const json_error_t *e) { + return (enum json_error_code)e->text[JSON_ERROR_TEXT_LENGTH - 1]; +} + +/* getters, setters, manipulation */ + +void json_object_seed(size_t seed); +size_t json_object_size(const json_t *object); +json_t *json_object_get(const json_t *object, const char *key); +int json_object_set_new(json_t *object, const char *key, json_t *value); +int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); +int json_object_del(json_t *object, const char *key); +int json_object_clear(json_t *object); +int json_object_update(json_t *object, json_t *other); +int json_object_update_existing(json_t *object, json_t *other); +int json_object_update_missing(json_t *object, json_t *other); +void *json_object_iter(json_t *object); +void *json_object_iter_at(json_t *object, const char *key); +void *json_object_key_to_iter(const char *key); +void *json_object_iter_next(json_t *object, void *iter); +const char *json_object_iter_key(void *iter); +json_t *json_object_iter_value(void *iter); +int json_object_iter_set_new(json_t *object, void *iter, json_t *value); + +#define json_object_foreach(object, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) + +#define json_object_foreach_safe(object, n, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) + +#define json_array_foreach(array, index, value) \ + for(index = 0; \ + index < json_array_size(array) && (value = json_array_get(array, index)); \ + index++) + +static JSON_INLINE +int json_object_set(json_t *object, const char *key, json_t *value) +{ + return json_object_set_new(object, key, json_incref(value)); +} + +static JSON_INLINE +int json_object_set_nocheck(json_t *object, const char *key, json_t *value) +{ + return json_object_set_new_nocheck(object, key, json_incref(value)); +} + +static JSON_INLINE +int json_object_iter_set(json_t *object, void *iter, json_t *value) +{ + return json_object_iter_set_new(object, iter, json_incref(value)); +} + +size_t json_array_size(const json_t *array); +json_t *json_array_get(const json_t *array, size_t index); +int json_array_set_new(json_t *array, size_t index, json_t *value); +int json_array_append_new(json_t *array, json_t *value); +int json_array_insert_new(json_t *array, size_t index, json_t *value); +int json_array_remove(json_t *array, size_t index); +int json_array_clear(json_t *array); +int json_array_extend(json_t *array, json_t *other); + +static JSON_INLINE +int json_array_set(json_t *array, size_t ind, json_t *value) +{ + return json_array_set_new(array, ind, json_incref(value)); +} + +static JSON_INLINE +int json_array_append(json_t *array, json_t *value) +{ + return json_array_append_new(array, json_incref(value)); +} + +static JSON_INLINE +int json_array_insert(json_t *array, size_t ind, json_t *value) +{ + return json_array_insert_new(array, ind, json_incref(value)); +} + +const char *json_string_value(const json_t *string); +size_t json_string_length(const json_t *string); +json_int_t json_integer_value(const json_t *integer); +double json_real_value(const json_t *real); +double json_number_value(const json_t *json); + +int json_string_set(json_t *string, const char *value); +int json_string_setn(json_t *string, const char *value, size_t len); +int json_string_set_nocheck(json_t *string, const char *value); +int json_string_setn_nocheck(json_t *string, const char *value, size_t len); +int json_integer_set(json_t *integer, json_int_t value); +int json_real_set(json_t *real, double value); + +/* pack, unpack */ + +json_t *json_pack(const char *fmt, ...); +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...); +json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap); + +#define JSON_VALIDATE_ONLY 0x1 +#define JSON_STRICT 0x2 + +int json_unpack(json_t *root, const char *fmt, ...); +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...); +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap); + +/* sprintf */ + +json_t *json_sprintf(const char *fmt, ...); +json_t *json_vsprintf(const char *fmt, va_list ap); + + +/* equality */ + +int json_equal(const json_t *value1, const json_t *value2); + + +/* copying */ + +json_t *json_copy(json_t *value); +json_t *json_deep_copy(const json_t *value); + +/* path */ + +json_t *json_path_get(const json_t *json, const char *path); +int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error); + +static JSON_INLINE +int json_path_set(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error) +{ + return json_path_set_new(json, path, json_incref(value), flags, error); +} + +/* decoding */ + +#define JSON_REJECT_DUPLICATES 0x1 +#define JSON_DISABLE_EOF_CHECK 0x2 +#define JSON_DECODE_ANY 0x4 +#define JSON_DECODE_INT_AS_REAL 0x8 +#define JSON_ALLOW_NUL 0x10 + +typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); + +json_t *json_loads(const char *input, size_t flags, json_error_t *error); +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error); +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error); +json_t *json_loadfd(int input, size_t flags, json_error_t *error); +json_t *json_load_file(const char *path, size_t flags, json_error_t *error); +json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error); + + +/* encoding */ + +#define JSON_MAX_INDENT 0x1F +#define JSON_INDENT(n) ((n) & JSON_MAX_INDENT) +#define JSON_COMPACT 0x20 +#define JSON_ENSURE_ASCII 0x40 +#define JSON_SORT_KEYS 0x80 +#define JSON_PRESERVE_ORDER 0x100 +#define JSON_ENCODE_ANY 0x200 +#define JSON_ESCAPE_SLASH 0x400 +#define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11) +#define JSON_EMBED 0x10000 + +typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); + +char *json_dumps(const json_t *json, size_t flags); +size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags); +int json_dumpf(const json_t *json, FILE *output, size_t flags); +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 json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags); + +/* custom memory allocation */ + +typedef void *(*json_malloc_t)(size_t); +typedef void (*json_free_t)(void *); + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn); +void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/client/jansson/jansson_config.h b/client/jansson/jansson_config.h new file mode 100644 index 00000000..b34baa55 --- /dev/null +++ b/client/jansson/jansson_config.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The configure script copies this file to jansson_config.h and + * replaces @var@ substitutions by values that fit your system. If you + * cannot run the configure script, you can do the value substitution + * by hand. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE inline +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#define JSON_INTEGER_IS_LONG_LONG 1 + +/* If locale.h and localeconv() are available, define to 1, + otherwise to 0. */ +#define JSON_HAVE_LOCALECONV 1 + +/* If __atomic builtins are available they will be used to manage + reference counts of json_t. */ +#define JSON_HAVE_ATOMIC_BUILTINS 1 + +/* If __atomic builtins are not available we try using __sync builtins + to manage reference counts of json_t. */ +#define JSON_HAVE_SYNC_BUILTINS 1 + +/* Maximum recursion depth for parsing JSON input. + This limits the depth of e.g. array-within-array constructions. */ +#define JSON_PARSER_MAX_DEPTH 2048 + +#endif diff --git a/client/jansson/jansson_config.h.in b/client/jansson/jansson_config.h.in new file mode 100644 index 00000000..fe692ab4 --- /dev/null +++ b/client/jansson/jansson_config.h.in @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * + * This file specifies a part of the site-specific configuration for + * Jansson, namely those things that affect the public API in + * jansson.h. + * + * The configure script copies this file to jansson_config.h and + * replaces @var@ substitutions by values that fit your system. If you + * cannot run the configure script, you can do the value substitution + * by hand. + */ + +#ifndef JANSSON_CONFIG_H +#define JANSSON_CONFIG_H + +/* If your compiler supports the inline keyword in C, JSON_INLINE is + defined to `inline', otherwise empty. In C++, the inline is always + supported. */ +#ifdef __cplusplus +#define JSON_INLINE inline +#else +#define JSON_INLINE @json_inline@ +#endif + +/* If your compiler supports the `long long` type and the strtoll() + library function, JSON_INTEGER_IS_LONG_LONG is defined to 1, + otherwise to 0. */ +#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@ + +/* If locale.h and localeconv() are available, define to 1, + otherwise to 0. */ +#define JSON_HAVE_LOCALECONV @json_have_localeconv@ + +/* If __atomic builtins are available they will be used to manage + reference counts of json_t. */ +#define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@ + +/* If __atomic builtins are not available we try using __sync builtins + to manage reference counts of json_t. */ +#define JSON_HAVE_SYNC_BUILTINS @json_have_sync_builtins@ + +/* Maximum recursion depth for parsing JSON input. + This limits the depth of e.g. array-within-array constructions. */ +#define JSON_PARSER_MAX_DEPTH 2048 + +#endif diff --git a/client/jansson/jansson_private.h b/client/jansson/jansson_private.h new file mode 100644 index 00000000..7b0985aa --- /dev/null +++ b/client/jansson/jansson_private.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef JANSSON_PRIVATE_H +#define JANSSON_PRIVATE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "jansson.h" +#include "hashtable.h" +#include "strbuffer.h" + +#define container_of(ptr_, type_, member_) \ + ((type_ *)((char *)ptr_ - offsetof(type_, member_))) + +/* On some platforms, max() may already be defined */ +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* va_copy is a C99 feature. In C89 implementations, it's sometimes + available as __va_copy. If not, memcpy() should do the trick. */ +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list)) +#endif +#endif + +typedef struct { + json_t json; + hashtable_t hashtable; +} json_object_t; + +typedef struct { + json_t json; + size_t size; + size_t entries; + json_t **table; +} json_array_t; + +typedef struct { + json_t json; + char *value; + size_t length; +} json_string_t; + +typedef struct { + json_t json; + double value; +} json_real_t; + +typedef struct { + json_t json; + json_int_t value; +} json_integer_t; + +#define json_to_object(json_) container_of(json_, json_object_t, json) +#define json_to_array(json_) container_of(json_, json_array_t, json) +#define json_to_string(json_) container_of(json_, json_string_t, json) +#define json_to_real(json_) container_of(json_, json_real_t, json) +#define json_to_integer(json_) container_of(json_, json_integer_t, json) + +/* Create a string by taking ownership of an existing buffer */ +json_t *jsonp_stringn_nocheck_own(const char *value, size_t len); + +/* Error message formatting */ +void jsonp_error_init(json_error_t *error, const char *source); +void jsonp_error_set_source(json_error_t *error, const char *source); +void jsonp_error_set(json_error_t *error, int line, int column, + size_t position, enum json_error_code code, + const char *msg, ...); +void jsonp_error_vset(json_error_t *error, int line, int column, + size_t position, enum json_error_code code, + const char *msg, va_list ap); + +/* Locale independent string<->double conversions */ +int jsonp_strtod(strbuffer_t *strbuffer, double *out); +int jsonp_dtostr(char *buffer, size_t size, double value, int prec); + +/* Wrappers for custom memory functions */ +void* jsonp_malloc(size_t size); +void jsonp_free(void *ptr); +char *jsonp_strndup(const char *str, size_t length); +char *jsonp_strdup(const char *str); +char *jsonp_strndup(const char *str, size_t len); + + +/* Windows compatibility */ +#if defined(_WIN32) || defined(WIN32) +# if defined(_MSC_VER) /* MS compiller */ +# if (_MSC_VER < 1900) && !defined(snprintf) /* snprintf not defined yet & not introduced */ +# define snprintf _snprintf +# endif +# if (_MSC_VER < 1500) && !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */ +# define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a) +# endif +# else /* Other Windows compiller, old definition */ +# define snprintf _snprintf +# define vsnprintf _vsnprintf +# endif +#endif + +#endif diff --git a/client/jansson/load.c b/client/jansson/load.c new file mode 100644 index 00000000..4cf3855a --- /dev/null +++ b/client/jansson/load.c @@ -0,0 +1,1155 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "jansson_private.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "jansson.h" +#include "strbuffer.h" +#include "utf.h" + +#define STREAM_STATE_OK 0 +#define STREAM_STATE_EOF -1 +#define STREAM_STATE_ERROR -2 + +#define TOKEN_INVALID -1 +#define TOKEN_EOF 0 +#define TOKEN_STRING 256 +#define TOKEN_INTEGER 257 +#define TOKEN_REAL 258 +#define TOKEN_TRUE 259 +#define TOKEN_FALSE 260 +#define TOKEN_NULL 261 + +/* Locale independent versions of isxxx() functions */ +#define l_isupper(c) ('A' <= (c) && (c) <= 'Z') +#define l_islower(c) ('a' <= (c) && (c) <= 'z') +#define l_isalpha(c) (l_isupper(c) || l_islower(c)) +#define l_isdigit(c) ('0' <= (c) && (c) <= '9') +#define l_isxdigit(c) \ + (l_isdigit(c) || ('A' <= (c) && (c) <= 'F') || ('a' <= (c) && (c) <= 'f')) + +/* Read one byte from stream, convert to unsigned char, then int, and + return. return EOF on end of file. This corresponds to the + behaviour of fgetc(). */ +typedef int (*get_func)(void *data); + +typedef struct { + get_func get; + void *data; + char buffer[5]; + size_t buffer_pos; + int state; + int line; + int column, last_column; + size_t position; +} stream_t; + +typedef struct { + stream_t stream; + strbuffer_t saved_text; + size_t flags; + size_t depth; + int token; + union { + struct { + char *val; + size_t len; + } string; + json_int_t integer; + double real; + } value; +} lex_t; + +#define stream_to_lex(stream) container_of(stream, lex_t, stream) + + +/*** error reporting ***/ + +static void error_set(json_error_t *error, const lex_t *lex, + enum json_error_code code, + const char *msg, ...) +{ + va_list ap; + char msg_text[JSON_ERROR_TEXT_LENGTH]; + char msg_with_context[JSON_ERROR_TEXT_LENGTH + 28]; + + int line = -1, col = -1; + size_t pos = 0; + const char *result = msg_text; + + if(!error) + return; + + va_start(ap, msg); + vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap); + msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + va_end(ap); + + if(lex) + { + const char *saved_text = strbuffer_value(&lex->saved_text); + + line = lex->stream.line; + col = lex->stream.column; + pos = lex->stream.position; + + if(saved_text && saved_text[0]) + { + if(lex->saved_text.length <= 20) { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH + 28, + "%s near '%s'", msg_text, saved_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } + else + { + if(code == json_error_invalid_syntax) { + /* More specific error code for premature end of file. */ + code = json_error_premature_end_of_input; + } + if(lex->stream.state == STREAM_STATE_ERROR) { + /* No context for UTF-8 decoding errors */ + result = msg_text; + } + else { + snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH + 17, + "%s near end of file", msg_text); + msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0'; + result = msg_with_context; + } + } + } + + jsonp_error_set(error, line, col, pos, code, "%s", result); +} + + +/*** lexical analyzer ***/ + +static void +stream_init(stream_t *stream, get_func get, void *data) +{ + stream->get = get; + stream->data = data; + stream->buffer[0] = '\0'; + stream->buffer_pos = 0; + + stream->state = STREAM_STATE_OK; + stream->line = 1; + stream->column = 0; + stream->position = 0; +} + +static int stream_get(stream_t *stream, json_error_t *error) +{ + int c; + + if(stream->state != STREAM_STATE_OK) + return stream->state; + + if(!stream->buffer[stream->buffer_pos]) + { + c = stream->get(stream->data); + if(c == EOF) { + stream->state = STREAM_STATE_EOF; + return STREAM_STATE_EOF; + } + + stream->buffer[0] = c; + stream->buffer_pos = 0; + + if(0x80 <= c && c <= 0xFF) + { + /* multi-byte UTF-8 sequence */ + size_t i, count; + + count = utf8_check_first(c); + if(!count) + goto out; + + assert(count >= 2); + + for(i = 1; i < count; i++) + stream->buffer[i] = stream->get(stream->data); + + if(!utf8_check_full(stream->buffer, count, NULL)) + goto out; + + stream->buffer[count] = '\0'; + } + else + stream->buffer[1] = '\0'; + } + + c = stream->buffer[stream->buffer_pos++]; + + stream->position++; + if(c == '\n') { + stream->line++; + stream->last_column = stream->column; + stream->column = 0; + } + else if(utf8_check_first(c)) { + /* track the Unicode character column, so increment only if + this is the first character of a UTF-8 sequence */ + stream->column++; + } + + return c; + +out: + stream->state = STREAM_STATE_ERROR; + error_set(error, stream_to_lex(stream), json_error_invalid_utf8, "unable to decode byte 0x%x", c); + return STREAM_STATE_ERROR; +} + +static void stream_unget(stream_t *stream, int c) +{ + if(c == STREAM_STATE_EOF || c == STREAM_STATE_ERROR) + return; + + stream->position--; + if(c == '\n') { + stream->line--; + stream->column = stream->last_column; + } + else if(utf8_check_first(c)) + stream->column--; + + assert(stream->buffer_pos > 0); + stream->buffer_pos--; + assert(stream->buffer[stream->buffer_pos] == c); +} + + +static int lex_get(lex_t *lex, json_error_t *error) +{ + return stream_get(&lex->stream, error); +} + +static void lex_save(lex_t *lex, int c) +{ + strbuffer_append_byte(&lex->saved_text, c); +} + +static int lex_get_save(lex_t *lex, json_error_t *error) +{ + int c = stream_get(&lex->stream, error); + if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) + lex_save(lex, c); + return c; +} + +static void lex_unget(lex_t *lex, int c) +{ + stream_unget(&lex->stream, c); +} + +static void lex_unget_unsave(lex_t *lex, int c) +{ + if(c != STREAM_STATE_EOF && c != STREAM_STATE_ERROR) { + /* Since we treat warnings as errors, when assertions are turned + * off the "d" variable would be set but never used. Which is + * treated as an error by GCC. + */ + #ifndef NDEBUG + char d; + #endif + stream_unget(&lex->stream, c); + #ifndef NDEBUG + d = + #endif + strbuffer_pop(&lex->saved_text); + assert(c == d); + } +} + +static void lex_save_cached(lex_t *lex) +{ + while(lex->stream.buffer[lex->stream.buffer_pos] != '\0') + { + lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]); + lex->stream.buffer_pos++; + lex->stream.position++; + } +} + +static void lex_free_string(lex_t *lex) +{ + jsonp_free(lex->value.string.val); + lex->value.string.val = NULL; + lex->value.string.len = 0; +} + +/* assumes that str points to 'u' plus at least 4 valid hex digits */ +static int32_t decode_unicode_escape(const char *str) +{ + int i; + int32_t value = 0; + + assert(str[0] == 'u'); + + for(i = 1; i <= 4; i++) { + char c = str[i]; + value <<= 4; + if(l_isdigit(c)) + value += c - '0'; + else if(l_islower(c)) + value += c - 'a' + 10; + else if(l_isupper(c)) + value += c - 'A' + 10; + else + return -1; + } + + return value; +} + +static void lex_scan_string(lex_t *lex, json_error_t *error) +{ + int c; + const char *p; + char *t; + int i; + + lex->value.string.val = NULL; + lex->token = TOKEN_INVALID; + + c = lex_get_save(lex, error); + + while(c != '"') { + if(c == STREAM_STATE_ERROR) + goto out; + + else if(c == STREAM_STATE_EOF) { + error_set(error, lex, json_error_premature_end_of_input, "premature end of input"); + goto out; + } + + else if(0 <= c && c <= 0x1F) { + /* control character */ + lex_unget_unsave(lex, c); + if(c == '\n') + error_set(error, lex, json_error_invalid_syntax, "unexpected newline"); + else + error_set(error, lex, json_error_invalid_syntax, "control character 0x%x", c); + goto out; + } + + else if(c == '\\') { + c = lex_get_save(lex, error); + if(c == 'u') { + c = lex_get_save(lex, error); + for(i = 0; i < 4; i++) { + if(!l_isxdigit(c)) { + error_set(error, lex, json_error_invalid_syntax, "invalid escape"); + goto out; + } + c = lex_get_save(lex, error); + } + } + else if(c == '"' || c == '\\' || c == '/' || c == 'b' || + c == 'f' || c == 'n' || c == 'r' || c == 't') + c = lex_get_save(lex, error); + else { + error_set(error, lex, json_error_invalid_syntax, "invalid escape"); + goto out; + } + } + else + c = lex_get_save(lex, error); + } + + /* the actual value is at most of the same length as the source + string, because: + - shortcut escapes (e.g. "\t") (length 2) are converted to 1 byte + - a single \uXXXX escape (length 6) is converted to at most 3 bytes + - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair + are converted to 4 bytes + */ + t = jsonp_malloc(lex->saved_text.length + 1); + if(!t) { + /* this is not very nice, since TOKEN_INVALID is returned */ + goto out; + } + lex->value.string.val = t; + + /* + 1 to skip the " */ + p = strbuffer_value(&lex->saved_text) + 1; + + while(*p != '"') { + if(*p == '\\') { + p++; + if(*p == 'u') { + size_t length; + int32_t value; + + value = decode_unicode_escape(p); + if(value < 0) { + error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1); + goto out; + } + p += 5; + + if(0xD800 <= value && value <= 0xDBFF) { + /* surrogate pair */ + if(*p == '\\' && *(p + 1) == 'u') { + int32_t value2 = decode_unicode_escape(++p); + if(value2 < 0) { + error_set(error, lex, json_error_invalid_syntax, "invalid Unicode escape '%.6s'", p - 1); + goto out; + } + p += 5; + + if(0xDC00 <= value2 && value2 <= 0xDFFF) { + /* valid second surrogate */ + value = + ((value - 0xD800) << 10) + + (value2 - 0xDC00) + + 0x10000; + } + else { + /* invalid second surrogate */ + error_set(error, lex, + json_error_invalid_syntax, + "invalid Unicode '\\u%04X\\u%04X'", + value, value2); + goto out; + } + } + else { + /* no second surrogate */ + error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'", + value); + goto out; + } + } + else if(0xDC00 <= value && value <= 0xDFFF) { + error_set(error, lex, json_error_invalid_syntax, "invalid Unicode '\\u%04X'", value); + goto out; + } + + if(utf8_encode(value, t, &length)) + assert(0); + t += length; + } + else { + switch(*p) { + case '"': case '\\': case '/': + *t = *p; break; + case 'b': *t = '\b'; break; + case 'f': *t = '\f'; break; + case 'n': *t = '\n'; break; + case 'r': *t = '\r'; break; + case 't': *t = '\t'; break; + default: assert(0); + } + t++; + p++; + } + } + else + *(t++) = *(p++); + } + *t = '\0'; + lex->value.string.len = t - lex->value.string.val; + lex->token = TOKEN_STRING; + return; + +out: + lex_free_string(lex); +} + +#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ +#if JSON_INTEGER_IS_LONG_LONG +#ifdef _MSC_VER /* Microsoft Visual Studio */ +#define json_strtoint _strtoi64 +#else +#define json_strtoint strtoll +#endif +#else +#define json_strtoint strtol +#endif +#endif + +static int lex_scan_number(lex_t *lex, int c, json_error_t *error) +{ + const char *saved_text; + char *end; + double doubleval; + + lex->token = TOKEN_INVALID; + + if(c == '-') + c = lex_get_save(lex, error); + + if(c == '0') { + c = lex_get_save(lex, error); + if(l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + } + else if(l_isdigit(c)) { + do + c = lex_get_save(lex, error); + while(l_isdigit(c)); + } + else { + lex_unget_unsave(lex, c); + goto out; + } + + if(!(lex->flags & JSON_DECODE_INT_AS_REAL) && + c != '.' && c != 'E' && c != 'e') + { + json_int_t intval; + + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + errno = 0; + intval = json_strtoint(saved_text, &end, 10); + if(errno == ERANGE) { + if(intval < 0) + error_set(error, lex, json_error_numeric_overflow, "too big negative integer"); + else + error_set(error, lex, json_error_numeric_overflow, "too big integer"); + goto out; + } + + assert(end == saved_text + lex->saved_text.length); + + lex->token = TOKEN_INTEGER; + lex->value.integer = intval; + return 0; + } + + if(c == '.') { + c = lex_get(lex, error); + if(!l_isdigit(c)) { + lex_unget(lex, c); + goto out; + } + lex_save(lex, c); + + do + c = lex_get_save(lex, error); + while(l_isdigit(c)); + } + + if(c == 'E' || c == 'e') { + c = lex_get_save(lex, error); + if(c == '+' || c == '-') + c = lex_get_save(lex, error); + + if(!l_isdigit(c)) { + lex_unget_unsave(lex, c); + goto out; + } + + do + c = lex_get_save(lex, error); + while(l_isdigit(c)); + } + + lex_unget_unsave(lex, c); + + if(jsonp_strtod(&lex->saved_text, &doubleval)) { + error_set(error, lex, json_error_numeric_overflow, "real number overflow"); + goto out; + } + + lex->token = TOKEN_REAL; + lex->value.real = doubleval; + return 0; + +out: + return -1; +} + +static int lex_scan(lex_t *lex, json_error_t *error) +{ + int c; + + strbuffer_clear(&lex->saved_text); + + if(lex->token == TOKEN_STRING) + lex_free_string(lex); + + do + c = lex_get(lex, error); + while(c == ' ' || c == '\t' || c == '\n' || c == '\r'); + + if(c == STREAM_STATE_EOF) { + lex->token = TOKEN_EOF; + goto out; + } + + if(c == STREAM_STATE_ERROR) { + lex->token = TOKEN_INVALID; + goto out; + } + + lex_save(lex, c); + + if(c == '{' || c == '}' || c == '[' || c == ']' || c == ':' || c == ',') + lex->token = c; + + else if(c == '"') + lex_scan_string(lex, error); + + else if(l_isdigit(c) || c == '-') { + if(lex_scan_number(lex, c, error)) + goto out; + } + + else if(l_isalpha(c)) { + /* eat up the whole identifier for clearer error messages */ + const char *saved_text; + + do + c = lex_get_save(lex, error); + while(l_isalpha(c)); + lex_unget_unsave(lex, c); + + saved_text = strbuffer_value(&lex->saved_text); + + if(strcmp(saved_text, "true") == 0) + lex->token = TOKEN_TRUE; + else if(strcmp(saved_text, "false") == 0) + lex->token = TOKEN_FALSE; + else if(strcmp(saved_text, "null") == 0) + lex->token = TOKEN_NULL; + else + lex->token = TOKEN_INVALID; + } + + else { + /* save the rest of the input UTF-8 sequence to get an error + message of valid UTF-8 */ + lex_save_cached(lex); + lex->token = TOKEN_INVALID; + } + +out: + return lex->token; +} + +static char *lex_steal_string(lex_t *lex, size_t *out_len) +{ + char *result = NULL; + if(lex->token == TOKEN_STRING) { + result = lex->value.string.val; + *out_len = lex->value.string.len; + lex->value.string.val = NULL; + lex->value.string.len = 0; + } + return result; +} + +static int lex_init(lex_t *lex, get_func get, size_t flags, void *data) +{ + stream_init(&lex->stream, get, data); + if(strbuffer_init(&lex->saved_text)) + return -1; + + lex->flags = flags; + lex->token = TOKEN_INVALID; + return 0; +} + +static void lex_close(lex_t *lex) +{ + if(lex->token == TOKEN_STRING) + lex_free_string(lex); + strbuffer_close(&lex->saved_text); +} + + +/*** parser ***/ + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error); + +static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *object = json_object(); + if(!object) + return NULL; + + lex_scan(lex, error); + if(lex->token == '}') + return object; + + while(1) { + char *key; + size_t len; + json_t *value; + + if(lex->token != TOKEN_STRING) { + error_set(error, lex, json_error_invalid_syntax, "string or '}' expected"); + goto error; + } + + key = lex_steal_string(lex, &len); + if(!key) + return NULL; + if (memchr(key, '\0', len)) { + jsonp_free(key); + error_set(error, lex, json_error_null_byte_in_key, "NUL byte in object key not supported"); + goto error; + } + + if(flags & JSON_REJECT_DUPLICATES) { + if(json_object_get(object, key)) { + jsonp_free(key); + error_set(error, lex, json_error_duplicate_key, "duplicate object key"); + goto error; + } + } + + lex_scan(lex, error); + if(lex->token != ':') { + jsonp_free(key); + error_set(error, lex, json_error_invalid_syntax, "':' expected"); + goto error; + } + + lex_scan(lex, error); + value = parse_value(lex, flags, error); + if(!value) { + jsonp_free(key); + goto error; + } + + if(json_object_set_new_nocheck(object, key, value)) { + jsonp_free(key); + goto error; + } + + jsonp_free(key); + + lex_scan(lex, error); + if(lex->token != ',') + break; + + lex_scan(lex, error); + } + + if(lex->token != '}') { + error_set(error, lex, json_error_invalid_syntax, "'}' expected"); + goto error; + } + + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *parse_array(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *array = json_array(); + if(!array) + return NULL; + + lex_scan(lex, error); + if(lex->token == ']') + return array; + + while(lex->token) { + json_t *elem = parse_value(lex, flags, error); + if(!elem) + goto error; + + if(json_array_append_new(array, elem)) { + goto error; + } + + lex_scan(lex, error); + if(lex->token != ',') + break; + + lex_scan(lex, error); + } + + if(lex->token != ']') { + error_set(error, lex, json_error_invalid_syntax, "']' expected"); + goto error; + } + + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *json; + + lex->depth++; + if(lex->depth > JSON_PARSER_MAX_DEPTH) { + error_set(error, lex, json_error_stack_overflow, "maximum parsing depth reached"); + return NULL; + } + + switch(lex->token) { + case TOKEN_STRING: { + const char *value = lex->value.string.val; + size_t len = lex->value.string.len; + + if(!(flags & JSON_ALLOW_NUL)) { + if(memchr(value, '\0', len)) { + error_set(error, lex, json_error_null_character, "\\u0000 is not allowed without JSON_ALLOW_NUL"); + return NULL; + } + } + + json = jsonp_stringn_nocheck_own(value, len); + if(json) { + lex->value.string.val = NULL; + lex->value.string.len = 0; + } + break; + } + + case TOKEN_INTEGER: { + json = json_integer(lex->value.integer); + break; + } + + case TOKEN_REAL: { + json = json_real(lex->value.real); + break; + } + + case TOKEN_TRUE: + json = json_true(); + break; + + case TOKEN_FALSE: + json = json_false(); + break; + + case TOKEN_NULL: + json = json_null(); + break; + + case '{': + json = parse_object(lex, flags, error); + break; + + case '[': + json = parse_array(lex, flags, error); + break; + + case TOKEN_INVALID: + error_set(error, lex, json_error_invalid_syntax, "invalid token"); + return NULL; + + default: + error_set(error, lex, json_error_invalid_syntax, "unexpected token"); + return NULL; + } + + if(!json) + return NULL; + + lex->depth--; + return json; +} + +static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error) +{ + json_t *result; + + lex->depth = 0; + + lex_scan(lex, error); + if(!(flags & JSON_DECODE_ANY)) { + if(lex->token != '[' && lex->token != '{') { + error_set(error, lex, json_error_invalid_syntax, "'[' or '{' expected"); + return NULL; + } + } + + result = parse_value(lex, flags, error); + if(!result) + return NULL; + + if(!(flags & JSON_DISABLE_EOF_CHECK)) { + lex_scan(lex, error); + if(lex->token != TOKEN_EOF) { + error_set(error, lex, json_error_end_of_input_expected, "end of file expected"); + json_decref(result); + return NULL; + } + } + + if(error) { + /* Save the position even though there was no error */ + error->position = (int)lex->stream.position; + } + + return result; +} + +typedef struct +{ + const char *data; + size_t pos; +} string_data_t; + +static int string_get(void *data) +{ + char c; + string_data_t *stream = (string_data_t *)data; + c = stream->data[stream->pos]; + if(c == '\0') + return EOF; + else + { + stream->pos++; + return (unsigned char)c; + } +} + +json_t *json_loads(const char *string, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + string_data_t stream_data; + + jsonp_error_init(error, ""); + + if (string == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + stream_data.data = string; + stream_data.pos = 0; + + if(lex_init(&lex, string_get, flags, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +typedef struct +{ + const char *data; + size_t len; + size_t pos; +} buffer_data_t; + +static int buffer_get(void *data) +{ + char c; + buffer_data_t *stream = data; + if(stream->pos >= stream->len) + return EOF; + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + buffer_data_t stream_data; + + jsonp_error_init(error, ""); + + if (buffer == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + stream_data.data = buffer; + stream_data.pos = 0; + stream_data.len = buflen; + + if(lex_init(&lex, buffer_get, flags, (void *)&stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) +{ + lex_t lex; + const char *source; + json_t *result; + + if(input == stdin) + source = ""; + else + source = ""; + + jsonp_error_init(error, source); + + if (input == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)fgetc, flags, input)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +static int fd_get_func(int *fd) +{ + uint8_t c; +#ifdef HAVE_UNISTD_H + if (read(*fd, &c, 1) == 1) + return c; +#endif + return EOF; +} + +json_t *json_loadfd(int input, size_t flags, json_error_t *error) +{ + lex_t lex; + const char *source; + json_t *result; + +#ifdef HAVE_UNISTD_H + if(input == STDIN_FILENO) + source = ""; + else +#endif + source = ""; + + jsonp_error_init(error, source); + + if (input < 0) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)fd_get_func, flags, &input)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} + +json_t *json_load_file(const char *path, size_t flags, json_error_t *error) +{ + json_t *result; + FILE *fp; + + jsonp_error_init(error, path); + + if (path == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + fp = fopen(path, "rb"); + if(!fp) + { + error_set(error, NULL, json_error_cannot_open_file, "unable to open %s: %s", + path, strerror(errno)); + return NULL; + } + + result = json_loadf(fp, flags, error); + + fclose(fp); + return result; +} + +#define MAX_BUF_LEN 1024 + +typedef struct +{ + char data[MAX_BUF_LEN]; + size_t len; + size_t pos; + json_load_callback_t callback; + void *arg; +} callback_data_t; + +static int callback_get(void *data) +{ + char c; + callback_data_t *stream = data; + + if(stream->pos >= stream->len) { + stream->pos = 0; + stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg); + if(stream->len == 0 || stream->len == (size_t)-1) + return EOF; + } + + c = stream->data[stream->pos]; + stream->pos++; + return (unsigned char)c; +} + +json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error) +{ + lex_t lex; + json_t *result; + + callback_data_t stream_data; + + memset(&stream_data, 0, sizeof(stream_data)); + stream_data.callback = callback; + stream_data.arg = arg; + + jsonp_error_init(error, ""); + + if (callback == NULL) { + error_set(error, NULL, json_error_invalid_argument, "wrong arguments"); + return NULL; + } + + if(lex_init(&lex, (get_func)callback_get, flags, &stream_data)) + return NULL; + + result = parse_json(&lex, flags, error); + + lex_close(&lex); + return result; +} diff --git a/client/jansson/lookup3.h b/client/jansson/lookup3.h new file mode 100644 index 00000000..2fe4c258 --- /dev/null +++ b/client/jansson/lookup3.h @@ -0,0 +1,381 @@ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ + +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_STDINT_H +#include /* defines uint32_t etc */ +#endif + +#ifdef HAVE_SYS_PARAM_H +#include /* attempt to define endianness */ +#endif + +#ifdef HAVE_ENDIAN_H +# include /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef NO_MASKING_TRICK + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; /* fall through */ + case 11: c+=((uint32_t)k[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k[9])<<8; /* fall through */ + case 9 : c+=k[8]; /* fall through */ + case 8 : b+=((uint32_t)k[7])<<24; /* fall through */ + case 7 : b+=((uint32_t)k[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k[5])<<8; /* fall through */ + case 5 : b+=k[4]; /* fall through */ + case 4 : a+=((uint32_t)k[3])<<24; /* fall through */ + case 3 : a+=((uint32_t)k[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k[1])<<8; /* fall through */ + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} diff --git a/client/jansson/memory.c b/client/jansson/memory.c new file mode 100644 index 00000000..a2be5d23 --- /dev/null +++ b/client/jansson/memory.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * Copyright (c) 2011-2012 Basile Starynkevitch + * + * Jansson is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include + +#include "jansson.h" +#include "jansson_private.h" + +/* C89 allows these to be macros */ +#undef malloc +#undef free + +/* memory function pointers */ +static json_malloc_t do_malloc = malloc; +static json_free_t do_free = free; + +void *jsonp_malloc(size_t size) +{ + if(!size) + return NULL; + + return (*do_malloc)(size); +} + +void jsonp_free(void *ptr) +{ + if(!ptr) + return; + + (*do_free)(ptr); +} + +char *jsonp_strdup(const char *str) +{ + return jsonp_strndup(str, strlen(str)); +} + +char *jsonp_strndup(const char *str, size_t len) +{ + char *new_str; + + new_str = jsonp_malloc(len + 1); + if(!new_str) + return NULL; + + memcpy(new_str, str, len); + new_str[len] = '\0'; + return new_str; +} + +void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn) +{ + do_malloc = malloc_fn; + do_free = free_fn; +} + +void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn) +{ + if (malloc_fn) + *malloc_fn = do_malloc; + if (free_fn) + *free_fn = do_free; +} diff --git a/client/jansson/pack_unpack.c b/client/jansson/pack_unpack.c new file mode 100644 index 00000000..153f64d1 --- /dev/null +++ b/client/jansson/pack_unpack.c @@ -0,0 +1,909 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * Copyright (c) 2011-2012 Graeme Smecher + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include "jansson.h" +#include "jansson_private.h" +#include "utf.h" + +typedef struct { + int line; + int column; + size_t pos; + char token; +} token_t; + +typedef struct { + const char *start; + const char *fmt; + token_t prev_token; + token_t token; + token_t next_token; + json_error_t *error; + size_t flags; + int line; + int column; + size_t pos; + int has_error; +} scanner_t; + +#define token(scanner) ((scanner)->token.token) + +static const char * const type_names[] = { + "object", + "array", + "string", + "integer", + "real", + "true", + "false", + "null" +}; + +#define type_name(x) type_names[json_typeof(x)] + +static const char unpack_value_starters[] = "{[siIbfFOon"; + +static void scanner_init(scanner_t *s, json_error_t *error, + size_t flags, const char *fmt) +{ + s->error = error; + s->flags = flags; + s->fmt = s->start = fmt; + memset(&s->prev_token, 0, sizeof(token_t)); + memset(&s->token, 0, sizeof(token_t)); + memset(&s->next_token, 0, sizeof(token_t)); + s->line = 1; + s->column = 0; + s->pos = 0; + s->has_error = 0; +} + +static void next_token(scanner_t *s) +{ + const char *t; + s->prev_token = s->token; + + if(s->next_token.line) { + s->token = s->next_token; + s->next_token.line = 0; + return; + } + + t = s->fmt; + s->column++; + s->pos++; + + /* skip space and ignored chars */ + while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') { + if(*t == '\n') { + s->line++; + s->column = 1; + } + else + s->column++; + + s->pos++; + t++; + } + + s->token.token = *t; + s->token.line = s->line; + s->token.column = s->column; + s->token.pos = s->pos; + + t++; + s->fmt = t; +} + +static void prev_token(scanner_t *s) +{ + s->next_token = s->token; + s->token = s->prev_token; +} + +static void set_error(scanner_t *s, const char *source, enum json_error_code code, + const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos, + code, fmt, ap); + + jsonp_error_set_source(s->error, source); + + va_end(ap); +} + +static json_t *pack(scanner_t *s, va_list *ap); + + +/* ours will be set to 1 if jsonp_free() must be called for the result + afterwards */ +static char *read_string(scanner_t *s, va_list *ap, + const char *purpose, size_t *out_len, int *ours) +{ + char t; + strbuffer_t strbuff; + const char *str; + size_t length; + + next_token(s); + t = token(s); + prev_token(s); + + *ours = 0; + if(t != '#' && t != '%' && t != '+') { + /* Optimize the simple case */ + str = va_arg(*ap, const char *); + + if(!str) { + set_error(s, "", json_error_null_value, "NULL string argument"); + return NULL; + } + + length = strlen(str); + + if(!utf8_check_string(str, length)) { + set_error(s, "", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose); + return NULL; + } + + *out_len = length; + return (char *)str; + } + + strbuffer_init(&strbuff); + + while(1) { + str = va_arg(*ap, const char *); + if(!str) { + set_error(s, "", json_error_null_value, "NULL string argument"); + s->has_error = 1; + } + + next_token(s); + + if(token(s) == '#') { + length = va_arg(*ap, int); + } + else if(token(s) == '%') { + length = va_arg(*ap, size_t); + } + else { + prev_token(s); + length = s->has_error ? 0 : strlen(str); + } + + if(!s->has_error && strbuffer_append_bytes(&strbuff, str, length) == -1) { + set_error(s, "", json_error_out_of_memory, "Out of memory"); + s->has_error = 1; + } + + next_token(s); + if(token(s) != '+') { + prev_token(s); + break; + } + } + + if(s->has_error) { + strbuffer_close(&strbuff); + return NULL; + } + + if(!utf8_check_string(strbuff.value, strbuff.length)) { + set_error(s, "", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose); + strbuffer_close(&strbuff); + s->has_error = 1; + return NULL; + } + + *out_len = strbuff.length; + *ours = 1; + return strbuffer_steal_value(&strbuff); +} + +static json_t *pack_object(scanner_t *s, va_list *ap) +{ + json_t *object = json_object(); + next_token(s); + + while(token(s) != '}') { + char *key; + size_t len; + int ours; + json_t *value; + + if(!token(s)) { + set_error(s, "", json_error_invalid_format, "Unexpected end of format string"); + goto error; + } + + if(token(s) != 's') { + set_error(s, "", json_error_invalid_format, "Expected format 's', got '%c'", token(s)); + goto error; + } + + key = read_string(s, ap, "object key", &len, &ours); + if (!key) + s->has_error = 1; + + next_token(s); + + value = pack(s, ap); + if(!value) { + if(ours) + jsonp_free(key); + + if(strchr("soO", token(s)) && s->next_token.token == '*') { + next_token(s); + } else { + s->has_error = 1; + } + + next_token(s); + continue; + } + + if(s->has_error) + json_decref(value); + + if(!s->has_error && json_object_set_new_nocheck(object, key, value)) { + set_error(s, "", json_error_out_of_memory, "Unable to add key \"%s\"", key); + s->has_error = 1; + } + + if(ours) + jsonp_free(key); + + if(strchr("soO", token(s)) && s->next_token.token == '*') + next_token(s); + next_token(s); + } + + if(!s->has_error) + return object; + +error: + json_decref(object); + return NULL; +} + +static json_t *pack_array(scanner_t *s, va_list *ap) +{ + json_t *array = json_array(); + next_token(s); + + while(token(s) != ']') { + json_t *value; + + if(!token(s)) { + set_error(s, "", json_error_invalid_format, "Unexpected end of format string"); + /* Format string errors are unrecoverable. */ + goto error; + } + + value = pack(s, ap); + if(!value) { + if(strchr("soO", token(s)) && s->next_token.token == '*') { + next_token(s); + } else { + s->has_error = 1; + } + + next_token(s); + continue; + } + + if(s->has_error) + json_decref(value); + + if(!s->has_error && json_array_append_new(array, value)) { + set_error(s, "", json_error_out_of_memory, "Unable to append to array"); + s->has_error = 1; + } + + if(strchr("soO", token(s)) && s->next_token.token == '*') + next_token(s); + next_token(s); + } + + if(!s->has_error) + return array; + +error: + json_decref(array); + return NULL; +} + +static json_t *pack_string(scanner_t *s, va_list *ap) +{ + char *str; + size_t len; + int ours; + int nullable; + + next_token(s); + nullable = token(s) == '?'; + if (!nullable) + prev_token(s); + + str = read_string(s, ap, "string", &len, &ours); + if (!str) { + return nullable ? json_null() : NULL; + } else if (ours) { + return jsonp_stringn_nocheck_own(str, len); + } else { + return json_stringn_nocheck(str, len); + } +} + +static json_t *pack(scanner_t *s, va_list *ap) +{ + switch(token(s)) { + case '{': + return pack_object(s, ap); + + case '[': + return pack_array(s, ap); + + case 's': /* string */ + return pack_string(s, ap); + + case 'n': /* null */ + return json_null(); + + case 'b': /* boolean */ + return va_arg(*ap, int) ? json_true() : json_false(); + + case 'i': /* integer from int */ + return json_integer(va_arg(*ap, int)); + + case 'I': /* integer from json_int_t */ + return json_integer(va_arg(*ap, json_int_t)); + + case 'f': /* real */ + return json_real(va_arg(*ap, double)); + + case 'O': /* a json_t object; increments refcount */ + { + int nullable; + json_t *json; + + next_token(s); + nullable = token(s) == '?'; + if (!nullable) + prev_token(s); + + json = va_arg(*ap, json_t *); + if (!json && nullable) { + return json_null(); + } else { + return json_incref(json); + } + } + + case 'o': /* a json_t object; doesn't increment refcount */ + { + int nullable; + json_t *json; + + next_token(s); + nullable = token(s) == '?'; + if (!nullable) + prev_token(s); + + json = va_arg(*ap, json_t *); + if (!json && nullable) { + return json_null(); + } else { + return json; + } + } + + default: + set_error(s, "", json_error_invalid_format, "Unexpected format character '%c'", + token(s)); + s->has_error = 1; + return NULL; + } +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap); + +static int unpack_object(scanner_t *s, json_t *root, va_list *ap) +{ + int ret = -1; + int strict = 0; + int gotopt = 0; + + /* Use a set (emulated by a hashtable) to check that all object + keys are accessed. Checking that the correct number of keys + were accessed is not enough, as the same key can be unpacked + multiple times. + */ + hashtable_t key_set; + + if(hashtable_init(&key_set)) { + set_error(s, "", json_error_out_of_memory, "Out of memory"); + return -1; + } + + if(root && !json_is_object(root)) { + set_error(s, "", json_error_wrong_type, "Expected object, got %s", + type_name(root)); + goto out; + } + next_token(s); + + while(token(s) != '}') { + const char *key; + json_t *value; + int opt = 0; + + if(strict != 0) { + set_error(s, "", json_error_invalid_format, "Expected '}' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), token(s)); + goto out; + } + + if(!token(s)) { + set_error(s, "", json_error_invalid_format, "Unexpected end of format string"); + goto out; + } + + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); + next_token(s); + continue; + } + + if(token(s) != 's') { + set_error(s, "", json_error_invalid_format, "Expected format 's', got '%c'", token(s)); + goto out; + } + + key = va_arg(*ap, const char *); + if(!key) { + set_error(s, "", json_error_null_value, "NULL object key"); + goto out; + } + + next_token(s); + + if(token(s) == '?') { + opt = gotopt = 1; + next_token(s); + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_object_get(root, key); + if(!value && !opt) { + set_error(s, "", json_error_item_not_found, "Object item not found: %s", key); + goto out; + } + } + + if(unpack(s, value, ap)) + goto out; + + hashtable_set(&key_set, key, json_null()); + next_token(s); + } + + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if(root && strict == 1) { + /* We need to check that all non optional items have been parsed */ + const char *key; + int have_unrecognized_keys = 0; + strbuffer_t unrecognized_keys; + json_t *value; + long unpacked = 0; + if (gotopt) { + /* We have optional keys, we need to iter on each key */ + json_object_foreach(root, key, value) { + if(!hashtable_get(&key_set, key)) { + unpacked++; + + /* Save unrecognized keys for the error message */ + if (!have_unrecognized_keys) { + strbuffer_init(&unrecognized_keys); + have_unrecognized_keys = 1; + } else { + strbuffer_append_bytes(&unrecognized_keys, ", ", 2); + } + strbuffer_append_bytes(&unrecognized_keys, key, strlen(key)); + } + } + } else { + /* No optional keys, we can just compare the number of items */ + unpacked = (long)json_object_size(root) - (long)key_set.size; + } + if (unpacked) { + if (!gotopt) { + /* Save unrecognized keys for the error message */ + json_object_foreach(root, key, value) { + if(!hashtable_get(&key_set, key)) { + if (!have_unrecognized_keys) { + strbuffer_init(&unrecognized_keys); + have_unrecognized_keys = 1; + } else { + strbuffer_append_bytes(&unrecognized_keys, ", ", 2); + } + strbuffer_append_bytes(&unrecognized_keys, key, strlen(key)); + } + } + } + set_error(s, "", json_error_end_of_input_expected, + "%li object item(s) left unpacked: %s", + unpacked, strbuffer_value(&unrecognized_keys)); + strbuffer_close(&unrecognized_keys); + goto out; + } + } + + ret = 0; + +out: + hashtable_close(&key_set); + return ret; +} + +static int unpack_array(scanner_t *s, json_t *root, va_list *ap) +{ + size_t i = 0; + int strict = 0; + + if(root && !json_is_array(root)) { + set_error(s, "", json_error_wrong_type, "Expected array, got %s", type_name(root)); + return -1; + } + next_token(s); + + while(token(s) != ']') { + json_t *value; + + if(strict != 0) { + set_error(s, "", json_error_invalid_format, "Expected ']' after '%c', got '%c'", + (strict == 1 ? '!' : '*'), + token(s)); + return -1; + } + + if(!token(s)) { + set_error(s, "", json_error_invalid_format, "Unexpected end of format string"); + return -1; + } + + if(token(s) == '!' || token(s) == '*') { + strict = (token(s) == '!' ? 1 : -1); + next_token(s); + continue; + } + + if(!strchr(unpack_value_starters, token(s))) { + set_error(s, "", json_error_invalid_format, "Unexpected format character '%c'", + token(s)); + return -1; + } + + if(!root) { + /* skipping */ + value = NULL; + } + else { + value = json_array_get(root, i); + if(!value) { + set_error(s, "", json_error_index_out_of_range, "Array index %lu out of range", + (unsigned long)i); + return -1; + } + } + + if(unpack(s, value, ap)) + return -1; + + next_token(s); + i++; + } + + if(strict == 0 && (s->flags & JSON_STRICT)) + strict = 1; + + if(root && strict == 1 && i != json_array_size(root)) { + long diff = (long)json_array_size(root) - (long)i; + set_error(s, "", json_error_end_of_input_expected, "%li array item(s) left unpacked", diff); + return -1; + } + + return 0; +} + +static int unpack(scanner_t *s, json_t *root, va_list *ap) +{ + switch(token(s)) + { + case '{': + return unpack_object(s, root, ap); + + case '[': + return unpack_array(s, root, ap); + + case 's': + if(root && !json_is_string(root)) { + set_error(s, "", json_error_wrong_type, "Expected string, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + const char **str_target; + size_t *len_target = NULL; + + str_target = va_arg(*ap, const char **); + if(!str_target) { + set_error(s, "", json_error_null_value, "NULL string argument"); + return -1; + } + + next_token(s); + + if(token(s) == '%') { + len_target = va_arg(*ap, size_t *); + if(!len_target) { + set_error(s, "", json_error_null_value, "NULL string length argument"); + return -1; + } + } + else + prev_token(s); + + if(root) { + *str_target = json_string_value(root); + if(len_target) + *len_target = json_string_length(root); + } + } + return 0; + + case 'i': + if(root && !json_is_integer(root)) { + set_error(s, "", json_error_wrong_type, "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = (int)json_integer_value(root); + } + + return 0; + + case 'I': + if(root && !json_is_integer(root)) { + set_error(s, "", json_error_wrong_type, "Expected integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_int_t *target = va_arg(*ap, json_int_t*); + if(root) + *target = json_integer_value(root); + } + + return 0; + + case 'b': + if(root && !json_is_boolean(root)) { + set_error(s, "", json_error_wrong_type, "Expected true or false, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + int *target = va_arg(*ap, int*); + if(root) + *target = json_is_true(root); + } + + return 0; + + case 'f': + if(root && !json_is_real(root)) { + set_error(s, "", json_error_wrong_type, "Expected real, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_real_value(root); + } + + return 0; + + case 'F': + if(root && !json_is_number(root)) { + set_error(s, "", json_error_wrong_type, "Expected real or integer, got %s", + type_name(root)); + return -1; + } + + if(!(s->flags & JSON_VALIDATE_ONLY)) { + double *target = va_arg(*ap, double*); + if(root) + *target = json_number_value(root); + } + + return 0; + + case 'O': + if(root && !(s->flags & JSON_VALIDATE_ONLY)) + json_incref(root); + /* Fall through */ + + case 'o': + if(!(s->flags & JSON_VALIDATE_ONLY)) { + json_t **target = va_arg(*ap, json_t**); + if(root) + *target = root; + } + + return 0; + + case 'n': + /* Never assign, just validate */ + if(root && !json_is_null(root)) { + set_error(s, "", json_error_wrong_type, "Expected null, got %s", + type_name(root)); + return -1; + } + return 0; + + default: + set_error(s, "", json_error_invalid_format, "Unexpected format character '%c'", + token(s)); + return -1; + } +} + +json_t *json_vpack_ex(json_error_t *error, size_t flags, + const char *fmt, va_list ap) +{ + scanner_t s; + va_list ap_copy; + json_t *value; + + if(!fmt || !*fmt) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string"); + return NULL; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + value = pack(&s, &ap_copy); + va_end(ap_copy); + + if(!value) + return NULL; + + next_token(&s); + if(token(&s)) { + json_decref(value); + set_error(&s, "", json_error_invalid_format, "Garbage after format string"); + return NULL; + } + if(s.has_error) { + json_decref(value); + return NULL; + } + + return value; +} + +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(error, flags, fmt, ap); + va_end(ap); + + return value; +} + +json_t *json_pack(const char *fmt, ...) +{ + json_t *value; + va_list ap; + + va_start(ap, fmt); + value = json_vpack_ex(NULL, 0, fmt, ap); + va_end(ap); + + return value; +} + +int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, + const char *fmt, va_list ap) +{ + scanner_t s; + va_list ap_copy; + + if(!root) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, json_error_null_value, "NULL root value"); + return -1; + } + + if(!fmt || !*fmt) { + jsonp_error_init(error, ""); + jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string"); + return -1; + } + jsonp_error_init(error, NULL); + + scanner_init(&s, error, flags, fmt); + next_token(&s); + + va_copy(ap_copy, ap); + if(unpack(&s, root, &ap_copy)) { + va_end(ap_copy); + return -1; + } + va_end(ap_copy); + + next_token(&s); + if(token(&s)) { + set_error(&s, "", json_error_invalid_format, "Garbage after format string"); + return -1; + } + + return 0; +} + +int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, error, flags, fmt, ap); + va_end(ap); + + return ret; +} + +int json_unpack(json_t *root, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = json_vunpack_ex(root, NULL, 0, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/client/jansson/path.c b/client/jansson/path.c new file mode 100644 index 00000000..08f2da9f --- /dev/null +++ b/client/jansson/path.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2012 Rogerz Zhang + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + * + * source here https://github.com/rogerz/jansson/blob/json_path/src/path.c + */ + +#include +#include + +#include +#include "jansson_private.h" + + +json_t *json_path_get(const json_t *json, const char *path) +{ + static const char root_chr = '$', array_open = '['; + static const char *path_delims = ".[", *array_close = "]"; + const json_t *cursor; + char *token, *buf, *peek, *endptr, delim = '\0'; + const char *expect; + + if (!json || !path || path[0] != root_chr) + return NULL; + else + buf = jsonp_strdup(path); + + peek = buf + 1; + cursor = json; + token = NULL; + expect = path_delims; + + while (peek && *peek && cursor) + { + char *last_peek = peek; + peek = strpbrk(peek, expect); + if (peek) { + if (!token && peek != last_peek) + goto fail; + delim = *peek; + *peek++ = '\0'; + } else if (expect != path_delims || !token) { + goto fail; + } + + if (expect == path_delims) { + if (token) { + cursor = json_object_get(cursor, token); + } + expect = (delim == array_open ? array_close : path_delims); + token = peek; + } else if (expect == array_close) { + size_t index = strtol(token, &endptr, 0); + if (*endptr) + goto fail; + cursor = json_array_get(cursor, index); + token = NULL; + expect = path_delims; + } else { + goto fail; + } + } + + jsonp_free(buf); + return (json_t *)cursor; +fail: + jsonp_free(buf); + return NULL; +} + +int json_path_set_new(json_t *json, const char *path, json_t *value, size_t flags, json_error_t *error) +{ + static const char root_chr = '$', array_open = '[', object_delim = '.'; + static const char * const path_delims = ".[", *array_close = "]"; + + json_t *cursor, *parent = NULL; + char *token, *buf = NULL, *peek, delim = '\0'; + const char *expect; + int index_saved = -1; + + jsonp_error_init(error, ""); + + if (!json || !path || flags || !value) { + jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "invalid argument"); + goto fail; + } else { + buf = jsonp_strdup(path); + } + + if (buf[0] != root_chr) { + jsonp_error_set(error, -1, -1, 0, json_error_invalid_format, "path should start with $"); + goto fail; + } + + peek = buf + 1; + cursor = json; + token = NULL; + expect = path_delims; + + while (peek && *peek && cursor) + { + char *last_peek = peek; + peek = strpbrk(last_peek, expect); + + if (peek) { + if (!token && peek != last_peek) { + jsonp_error_set(error, -1, -1, last_peek - buf, json_error_invalid_format, "unexpected trailing chars"); + goto fail; + } + delim = *peek; + *peek++ = '\0'; + } else { // end of path + if (expect == path_delims) { + break; + } else { + jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "missing ']'?"); + goto fail; + } + } + + if (expect == path_delims) { + if (token) { + if (token[0] == '\0') { + jsonp_error_set(error, -1, -1, peek - buf, json_error_invalid_format, "empty token"); + goto fail; + } + + parent = cursor; + cursor = json_object_get(parent, token); + + if (!cursor) { + if (!json_is_object(parent)) { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected"); + goto fail; + } + if (delim == object_delim) { + cursor = json_object(); + json_object_set_new(parent, token, cursor); + } else { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "new array is not allowed"); + goto fail; + } + } + } + expect = (delim == array_open ? array_close : path_delims); + token = peek; + } else if (expect == array_close) { + char *endptr; + size_t index; + + parent = cursor; + if (!json_is_array(parent)) { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array expected"); + goto fail; + } + index = strtol(token, &endptr, 0); + if (*endptr) { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid array index"); + goto fail; + } + cursor = json_array_get(parent, index); + if (!cursor) { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "array index out of bound"); + goto fail; + } + index_saved = index; + token = NULL; + expect = path_delims; + } else { + assert(1); + jsonp_error_set(error, -1, -1, peek - buf, json_error_unknown, "unexpected error in path move"); + goto fail; + } + } + + if (token) { + if (json_is_object(cursor)) { + json_object_set(cursor, token, value); + } else { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "object expected"); + goto fail; + } + cursor = json_object_get(cursor, token); + } else if (index_saved != -1 && json_is_array(parent)) { + json_array_set(parent, index_saved, value); + cursor = json_array_get(parent, index_saved); + } else { + jsonp_error_set(error, -1, -1, peek - buf, json_error_item_not_found, "invalid path"); + goto fail; + } + + json_decref(value); + jsonp_free(buf); + return 0; + +fail: + json_decref(value); + jsonp_free(buf); + return -1; +} diff --git a/client/jansson/strbuffer.c b/client/jansson/strbuffer.c new file mode 100644 index 00000000..5e8c0039 --- /dev/null +++ b/client/jansson/strbuffer.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include "jansson_private.h" +#include "strbuffer.h" + +#define STRBUFFER_MIN_SIZE 16 +#define STRBUFFER_FACTOR 2 +#define STRBUFFER_SIZE_MAX ((size_t)-1) + +int strbuffer_init(strbuffer_t *strbuff) +{ + strbuff->size = STRBUFFER_MIN_SIZE; + strbuff->length = 0; + + strbuff->value = jsonp_malloc(strbuff->size); + if(!strbuff->value) + return -1; + + /* initialize to empty */ + strbuff->value[0] = '\0'; + return 0; +} + +void strbuffer_close(strbuffer_t *strbuff) +{ + if(strbuff->value) + jsonp_free(strbuff->value); + + strbuff->size = 0; + strbuff->length = 0; + strbuff->value = NULL; +} + +void strbuffer_clear(strbuffer_t *strbuff) +{ + strbuff->length = 0; + strbuff->value[0] = '\0'; +} + +const char *strbuffer_value(const strbuffer_t *strbuff) +{ + return strbuff->value; +} + +char *strbuffer_steal_value(strbuffer_t *strbuff) +{ + char *result = strbuff->value; + strbuff->value = NULL; + return result; +} + +int strbuffer_append_byte(strbuffer_t *strbuff, char byte) +{ + return strbuffer_append_bytes(strbuff, &byte, 1); +} + +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size) +{ + if(size >= strbuff->size - strbuff->length) + { + size_t new_size; + char *new_value; + + /* avoid integer overflow */ + if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR + || size > STRBUFFER_SIZE_MAX - 1 + || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size) + return -1; + + new_size = max(strbuff->size * STRBUFFER_FACTOR, + strbuff->length + size + 1); + + new_value = jsonp_malloc(new_size); + if(!new_value) + return -1; + + memcpy(new_value, strbuff->value, strbuff->length); + + jsonp_free(strbuff->value); + strbuff->value = new_value; + strbuff->size = new_size; + } + + memcpy(strbuff->value + strbuff->length, data, size); + strbuff->length += size; + strbuff->value[strbuff->length] = '\0'; + + return 0; +} + +char strbuffer_pop(strbuffer_t *strbuff) +{ + if(strbuff->length > 0) { + char c = strbuff->value[--strbuff->length]; + strbuff->value[strbuff->length] = '\0'; + return c; + } + else + return '\0'; +} diff --git a/client/jansson/strbuffer.h b/client/jansson/strbuffer.h new file mode 100644 index 00000000..615b7f5f --- /dev/null +++ b/client/jansson/strbuffer.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef STRBUFFER_H +#define STRBUFFER_H + +#include + +typedef struct { + char *value; + size_t length; /* bytes used */ + size_t size; /* bytes allocated */ +} strbuffer_t; + +int strbuffer_init(strbuffer_t *strbuff); +void strbuffer_close(strbuffer_t *strbuff); + +void strbuffer_clear(strbuffer_t *strbuff); + +const char *strbuffer_value(const strbuffer_t *strbuff); + +/* Steal the value and close the strbuffer */ +char *strbuffer_steal_value(strbuffer_t *strbuff); + +int strbuffer_append_byte(strbuffer_t *strbuff, char byte); +int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size); + +char strbuffer_pop(strbuffer_t *strbuff); + +#endif diff --git a/client/jansson/strconv.c b/client/jansson/strconv.c new file mode 100644 index 00000000..8075481e --- /dev/null +++ b/client/jansson/strconv.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#ifdef __MINGW32__ +#undef __NO_ISOCEXT /* ensure stdlib.h will declare prototypes for mingw own 'strtod' replacement, called '__strtod' */ +#endif +#include "jansson_private.h" +#include "strbuffer.h" + +/* need jansson_private_config.h to get the correct snprintf */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef __MINGW32__ +#define strtod __strtod +#endif + +#if JSON_HAVE_LOCALECONV +#include + +/* + - This code assumes that the decimal separator is exactly one + character. + + - If setlocale() is called by another thread between the call to + localeconv() and the call to sprintf() or strtod(), the result may + be wrong. setlocale() is not thread-safe and should not be used + this way. Multi-threaded programs should use uselocale() instead. +*/ + +static void to_locale(strbuffer_t *strbuffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(strbuffer->value, '.'); + if(pos) + *pos = *point; +} + +static void from_locale(char *buffer) +{ + const char *point; + char *pos; + + point = localeconv()->decimal_point; + if(*point == '.') { + /* No conversion needed */ + return; + } + + pos = strchr(buffer, *point); + if(pos) + *pos = '.'; +} +#endif + +int jsonp_strtod(strbuffer_t *strbuffer, double *out) +{ + double value; + char *end; + +#if JSON_HAVE_LOCALECONV + to_locale(strbuffer); +#endif + + errno = 0; + value = strtod(strbuffer->value, &end); + assert(end == strbuffer->value + strbuffer->length); + + if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) { + /* Overflow */ + return -1; + } + + *out = value; + return 0; +} + +int jsonp_dtostr(char *buffer, size_t size, double value, int precision) +{ + int ret; + char *start, *end; + size_t length; + + if (precision == 0) + precision = 17; + + ret = snprintf(buffer, size, "%.*g", precision, value); + if(ret < 0) + return -1; + + length = (size_t)ret; + if(length >= size) + return -1; + +#if JSON_HAVE_LOCALECONV + from_locale(buffer); +#endif + + /* Make sure there's a dot or 'e' in the output. Otherwise + a real is converted to an integer when decoding */ + if(strchr(buffer, '.') == NULL && + strchr(buffer, 'e') == NULL) + { + if(length + 3 >= size) { + /* No space to append ".0" */ + return -1; + } + buffer[length] = '.'; + buffer[length + 1] = '0'; + buffer[length + 2] = '\0'; + length += 2; + } + + /* Remove leading '+' from positive exponent. Also remove leading + zeros from exponents (added by some printf() implementations) */ + start = strchr(buffer, 'e'); + if(start) { + start++; + end = start + 1; + + if(*start == '-') + start++; + + while(*end == '0') + end++; + + if(end != start) { + memmove(start, end, length - (size_t)(end - buffer)); + length -= (size_t)(end - start); + } + } + + return (int)length; +} diff --git a/client/jansson/utf.c b/client/jansson/utf.c new file mode 100644 index 00000000..be966cbd --- /dev/null +++ b/client/jansson/utf.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include "utf.h" + +int utf8_encode(int32_t codepoint, char *buffer, size_t *size) +{ + if(codepoint < 0) + return -1; + else if(codepoint < 0x80) + { + buffer[0] = (char)codepoint; + *size = 1; + } + else if(codepoint < 0x800) + { + buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6); + buffer[1] = 0x80 + ((codepoint & 0x03F)); + *size = 2; + } + else if(codepoint < 0x10000) + { + buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12); + buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6); + buffer[2] = 0x80 + ((codepoint & 0x003F)); + *size = 3; + } + else if(codepoint <= 0x10FFFF) + { + buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18); + buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12); + buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6); + buffer[3] = 0x80 + ((codepoint & 0x00003F)); + *size = 4; + } + else + return -1; + + return 0; +} + +size_t utf8_check_first(char byte) +{ + unsigned char u = (unsigned char)byte; + + if(u < 0x80) + return 1; + + if(0x80 <= u && u <= 0xBF) { + /* second, third or fourth byte of a multi-byte + sequence, i.e. a "continuation byte" */ + return 0; + } + else if(u == 0xC0 || u == 0xC1) { + /* overlong encoding of an ASCII byte */ + return 0; + } + else if(0xC2 <= u && u <= 0xDF) { + /* 2-byte sequence */ + return 2; + } + + else if(0xE0 <= u && u <= 0xEF) { + /* 3-byte sequence */ + return 3; + } + else if(0xF0 <= u && u <= 0xF4) { + /* 4-byte sequence */ + return 4; + } + else { /* u >= 0xF5 */ + /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid + UTF-8 */ + return 0; + } +} + +size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint) +{ + size_t i; + int32_t value = 0; + unsigned char u = (unsigned char)buffer[0]; + + if(size == 2) + { + value = u & 0x1F; + } + else if(size == 3) + { + value = u & 0xF; + } + else if(size == 4) + { + value = u & 0x7; + } + else + return 0; + + for(i = 1; i < size; i++) + { + u = (unsigned char)buffer[i]; + + if(u < 0x80 || u > 0xBF) { + /* not a continuation byte */ + return 0; + } + + value = (value << 6) + (u & 0x3F); + } + + if(value > 0x10FFFF) { + /* not in Unicode range */ + return 0; + } + + else if(0xD800 <= value && value <= 0xDFFF) { + /* invalid code point (UTF-16 surrogate halves) */ + return 0; + } + + else if((size == 2 && value < 0x80) || + (size == 3 && value < 0x800) || + (size == 4 && value < 0x10000)) { + /* overlong encoding */ + return 0; + } + + if(codepoint) + *codepoint = value; + + return 1; +} + +const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint) +{ + size_t count; + int32_t value; + + if(!bufsize) + return buffer; + + count = utf8_check_first(buffer[0]); + if(count <= 0) + return NULL; + + if(count == 1) + value = (unsigned char)buffer[0]; + else + { + if(count > bufsize || !utf8_check_full(buffer, count, &value)) + return NULL; + } + + if(codepoint) + *codepoint = value; + + return buffer + count; +} + +int utf8_check_string(const char *string, size_t length) +{ + size_t i; + + for(i = 0; i < length; i++) + { + size_t count = utf8_check_first(string[i]); + if(count == 0) + return 0; + else if(count > 1) + { + if(count > length - i) + return 0; + + if(!utf8_check_full(&string[i], count, NULL)) + return 0; + + i += count - 1; + } + } + + return 1; +} diff --git a/client/jansson/utf.h b/client/jansson/utf.h new file mode 100644 index 00000000..e182df78 --- /dev/null +++ b/client/jansson/utf.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef UTF_H +#define UTF_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_STDINT_H +#include +#endif + +int utf8_encode(int32_t codepoint, char *buffer, size_t *size); + +size_t utf8_check_first(char byte); +size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint); +const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint); + +int utf8_check_string(const char *string, size_t length); + +#endif diff --git a/client/jansson/value.c b/client/jansson/value.c new file mode 100644 index 00000000..b3b31412 --- /dev/null +++ b/client/jansson/value.c @@ -0,0 +1,1078 @@ +/* + * Copyright (c) 2009-2016 Petri Lehtinen + * + * Jansson is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_STDINT_H +#include +#endif + +#include "jansson.h" +#include "hashtable.h" +#include "jansson_private.h" +#include "utf.h" + +/* Work around nonstandard isnan() and isinf() implementations */ +#ifndef isnan +#ifndef __sun +static JSON_INLINE int isnan(double x) { return x != x; } +#endif +#endif +#ifndef isinf +static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); } +#endif + +static JSON_INLINE void json_init(json_t *json, json_type type) +{ + json->type = type; + json->refcount = 1; +} + + +/*** object ***/ + +extern volatile uint32_t hashtable_seed; + +json_t *json_object(void) +{ + json_object_t *object = jsonp_malloc(sizeof(json_object_t)); + if(!object) + return NULL; + + if (!hashtable_seed) { + /* Autoseed */ + json_object_seed(0); + } + + json_init(&object->json, JSON_OBJECT); + + if(hashtable_init(&object->hashtable)) + { + jsonp_free(object); + return NULL; + } + + return &object->json; +} + +static void json_delete_object(json_object_t *object) +{ + hashtable_close(&object->hashtable); + jsonp_free(object); +} + +size_t json_object_size(const json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return 0; + + object = json_to_object(json); + return object->hashtable.size; +} + +json_t *json_object_get(const json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_get(&object->hashtable, key); +} + +int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) +{ + json_object_t *object; + + if(!value) + return -1; + + if(!key || !json_is_object(json) || json == value) + { + json_decref(value); + return -1; + } + object = json_to_object(json); + + if(hashtable_set(&object->hashtable, key, value)) + { + json_decref(value); + return -1; + } + + return 0; +} + +int json_object_set_new(json_t *json, const char *key, json_t *value) +{ + if(!key || !utf8_check_string(key, strlen(key))) + { + json_decref(value); + return -1; + } + + return json_object_set_new_nocheck(json, key, value); +} + +int json_object_del(json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return -1; + + object = json_to_object(json); + return hashtable_del(&object->hashtable, key); +} + +int json_object_clear(json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return -1; + + object = json_to_object(json); + hashtable_clear(&object->hashtable); + + return 0; +} + +int json_object_update(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(json_object_set_nocheck(object, key, value)) + return -1; + } + + return 0; +} + +int json_object_update_existing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(json_object_get(object, key)) + json_object_set_nocheck(object, key, value); + } + + return 0; +} + +int json_object_update_missing(json_t *object, json_t *other) +{ + const char *key; + json_t *value; + + if(!json_is_object(object) || !json_is_object(other)) + return -1; + + json_object_foreach(other, key, value) { + if(!json_object_get(object, key)) + json_object_set_nocheck(object, key, value); + } + + return 0; +} + +void *json_object_iter(json_t *json) +{ + json_object_t *object; + + if(!json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter(&object->hashtable); +} + +void *json_object_iter_at(json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter_at(&object->hashtable, key); +} + +void *json_object_iter_next(json_t *json, void *iter) +{ + json_object_t *object; + + if(!json_is_object(json) || iter == NULL) + return NULL; + + object = json_to_object(json); + return hashtable_iter_next(&object->hashtable, iter); +} + +const char *json_object_iter_key(void *iter) +{ + if(!iter) + return NULL; + + return hashtable_iter_key(iter); +} + +json_t *json_object_iter_value(void *iter) +{ + if(!iter) + return NULL; + + return (json_t *)hashtable_iter_value(iter); +} + +int json_object_iter_set_new(json_t *json, void *iter, json_t *value) +{ + if(!json_is_object(json) || !iter || !value) + { + json_decref(value); + return -1; + } + + hashtable_iter_set(iter, value); + return 0; +} + +void *json_object_key_to_iter(const char *key) +{ + if(!key) + return NULL; + + return hashtable_key_to_iter(key); +} + +static int json_object_equal(const json_t *object1, const json_t *object2) +{ + const char *key; + const json_t *value1, *value2; + + if(json_object_size(object1) != json_object_size(object2)) + return 0; + + json_object_foreach((json_t *)object1, key, value1) { + value2 = json_object_get(object2, key); + + if(!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_object_copy(json_t *object) +{ + json_t *result; + + const char *key; + json_t *value; + + result = json_object(); + if(!result) + return NULL; + + json_object_foreach(object, key, value) + json_object_set_nocheck(result, key, value); + + return result; +} + +static json_t *json_object_deep_copy(const json_t *object) +{ + json_t *result; + void *iter; + + result = json_object(); + if(!result) + return NULL; + + /* Cannot use json_object_foreach because object has to be cast + non-const */ + iter = json_object_iter((json_t *)object); + while(iter) { + const char *key; + const json_t *value; + key = json_object_iter_key(iter); + value = json_object_iter_value(iter); + + json_object_set_new_nocheck(result, key, json_deep_copy(value)); + iter = json_object_iter_next((json_t *)object, iter); + } + + return result; +} + + +/*** array ***/ + +json_t *json_array(void) +{ + json_array_t *array = jsonp_malloc(sizeof(json_array_t)); + if(!array) + return NULL; + json_init(&array->json, JSON_ARRAY); + + array->entries = 0; + array->size = 8; + + array->table = jsonp_malloc(array->size * sizeof(json_t *)); + if(!array->table) { + jsonp_free(array); + return NULL; + } + + return &array->json; +} + +static void json_delete_array(json_array_t *array) +{ + size_t i; + + for(i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + jsonp_free(array->table); + jsonp_free(array); +} + +size_t json_array_size(const json_t *json) +{ + if(!json_is_array(json)) + return 0; + + return json_to_array(json)->entries; +} + +json_t *json_array_get(const json_t *json, size_t index) +{ + json_array_t *array; + if(!json_is_array(json)) + return NULL; + array = json_to_array(json); + + if(index >= array->entries) + return NULL; + + return array->table[index]; +} + +int json_array_set_new(json_t *json, size_t index, json_t *value) +{ + json_array_t *array; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) + { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(index >= array->entries) + { + json_decref(value); + return -1; + } + + json_decref(array->table[index]); + array->table[index] = value; + + return 0; +} + +static void array_move(json_array_t *array, size_t dest, + size_t src, size_t count) +{ + memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *)); +} + +static void array_copy(json_t **dest, size_t dpos, + json_t **src, size_t spos, + size_t count) +{ + memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *)); +} + +static json_t **json_array_grow(json_array_t *array, + size_t amount, + int copy) +{ + size_t new_size; + json_t **old_table, **new_table; + + if(array->entries + amount <= array->size) + return array->table; + + old_table = array->table; + + new_size = max(array->size + amount, array->size * 2); + new_table = jsonp_malloc(new_size * sizeof(json_t *)); + if(!new_table) + return NULL; + + array->size = new_size; + array->table = new_table; + + if(copy) { + array_copy(array->table, 0, old_table, 0, array->entries); + jsonp_free(old_table); + return array->table; + } + + return old_table; +} + +int json_array_append_new(json_t *json, json_t *value) +{ + json_array_t *array; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) + { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(!json_array_grow(array, 1, 1)) { + json_decref(value); + return -1; + } + + array->table[array->entries] = value; + array->entries++; + + return 0; +} + +int json_array_insert_new(json_t *json, size_t index, json_t *value) +{ + json_array_t *array; + json_t **old_table; + + if(!value) + return -1; + + if(!json_is_array(json) || json == value) { + json_decref(value); + return -1; + } + array = json_to_array(json); + + if(index > array->entries) { + json_decref(value); + return -1; + } + + old_table = json_array_grow(array, 1, 0); + if(!old_table) { + json_decref(value); + return -1; + } + + if(old_table != array->table) { + array_copy(array->table, 0, old_table, 0, index); + array_copy(array->table, index + 1, old_table, index, + array->entries - index); + jsonp_free(old_table); + } + else + array_move(array, index + 1, index, array->entries - index); + + array->table[index] = value; + array->entries++; + + return 0; +} + +int json_array_remove(json_t *json, size_t index) +{ + json_array_t *array; + + if(!json_is_array(json)) + return -1; + array = json_to_array(json); + + if(index >= array->entries) + return -1; + + json_decref(array->table[index]); + + /* If we're removing the last element, nothing has to be moved */ + if(index < array->entries - 1) + array_move(array, index, index + 1, array->entries - index - 1); + + array->entries--; + + return 0; +} + +int json_array_clear(json_t *json) +{ + json_array_t *array; + size_t i; + + if(!json_is_array(json)) + return -1; + array = json_to_array(json); + + for(i = 0; i < array->entries; i++) + json_decref(array->table[i]); + + array->entries = 0; + return 0; +} + +int json_array_extend(json_t *json, json_t *other_json) +{ + json_array_t *array, *other; + size_t i; + + if(!json_is_array(json) || !json_is_array(other_json)) + return -1; + array = json_to_array(json); + other = json_to_array(other_json); + + if(!json_array_grow(array, other->entries, 1)) + return -1; + + for(i = 0; i < other->entries; i++) + json_incref(other->table[i]); + + array_copy(array->table, array->entries, other->table, 0, other->entries); + + array->entries += other->entries; + return 0; +} + +static int json_array_equal(const json_t *array1, const json_t *array2) +{ + size_t i, size; + + size = json_array_size(array1); + if(size != json_array_size(array2)) + return 0; + + for(i = 0; i < size; i++) + { + json_t *value1, *value2; + + value1 = json_array_get(array1, i); + value2 = json_array_get(array2, i); + + if(!json_equal(value1, value2)) + return 0; + } + + return 1; +} + +static json_t *json_array_copy(json_t *array) +{ + json_t *result; + size_t i; + + result = json_array(); + if(!result) + return NULL; + + for(i = 0; i < json_array_size(array); i++) + json_array_append(result, json_array_get(array, i)); + + return result; +} + +static json_t *json_array_deep_copy(const json_t *array) +{ + json_t *result; + size_t i; + + result = json_array(); + if(!result) + return NULL; + + for(i = 0; i < json_array_size(array); i++) + json_array_append_new(result, json_deep_copy(json_array_get(array, i))); + + return result; +} + +/*** string ***/ + +static json_t *string_create(const char *value, size_t len, int own) +{ + char *v; + json_string_t *string; + + if(!value) + return NULL; + + if(own) + v = (char *)value; + else { + v = jsonp_strndup(value, len); + if(!v) + return NULL; + } + + string = jsonp_malloc(sizeof(json_string_t)); + if(!string) { + if(!own) + jsonp_free(v); + return NULL; + } + json_init(&string->json, JSON_STRING); + string->value = v; + string->length = len; + + return &string->json; +} + +json_t *json_string_nocheck(const char *value) +{ + if(!value) + return NULL; + + return string_create(value, strlen(value), 0); +} + +json_t *json_stringn_nocheck(const char *value, size_t len) +{ + return string_create(value, len, 0); +} + +/* this is private; "steal" is not a public API concept */ +json_t *jsonp_stringn_nocheck_own(const char *value, size_t len) +{ + return string_create(value, len, 1); +} + +json_t *json_string(const char *value) +{ + if(!value) + return NULL; + + return json_stringn(value, strlen(value)); +} + +json_t *json_stringn(const char *value, size_t len) +{ + if(!value || !utf8_check_string(value, len)) + return NULL; + + return json_stringn_nocheck(value, len); +} + +const char *json_string_value(const json_t *json) +{ + if(!json_is_string(json)) + return NULL; + + return json_to_string(json)->value; +} + +size_t json_string_length(const json_t *json) +{ + if(!json_is_string(json)) + return 0; + + return json_to_string(json)->length; +} + +int json_string_set_nocheck(json_t *json, const char *value) +{ + if(!value) + return -1; + + return json_string_setn_nocheck(json, value, strlen(value)); +} + +int json_string_setn_nocheck(json_t *json, const char *value, size_t len) +{ + char *dup; + json_string_t *string; + + if(!json_is_string(json) || !value) + return -1; + + dup = jsonp_strndup(value, len); + if(!dup) + return -1; + + string = json_to_string(json); + jsonp_free(string->value); + string->value = dup; + string->length = len; + + return 0; +} + +int json_string_set(json_t *json, const char *value) +{ + if(!value) + return -1; + + return json_string_setn(json, value, strlen(value)); +} + +int json_string_setn(json_t *json, const char *value, size_t len) +{ + if(!value || !utf8_check_string(value, len)) + return -1; + + return json_string_setn_nocheck(json, value, len); +} + +static void json_delete_string(json_string_t *string) +{ + jsonp_free(string->value); + jsonp_free(string); +} + +static int json_string_equal(const json_t *string1, const json_t *string2) +{ + json_string_t *s1, *s2; + + if(!json_is_string(string1) || !json_is_string(string2)) + return 0; + + s1 = json_to_string(string1); + s2 = json_to_string(string2); + return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length); +} + +static json_t *json_string_copy(const json_t *string) +{ + json_string_t *s; + + if(!json_is_string(string)) + return NULL; + + s = json_to_string(string); + return json_stringn_nocheck(s->value, s->length); +} + +json_t *json_vsprintf(const char *fmt, va_list ap) { + int length; + char *buf; + va_list aq; + va_copy(aq, ap); + + length = vsnprintf(NULL, 0, fmt, ap); + if (length == 0) + return json_string(""); + + buf = jsonp_malloc(length + 1); + if (!buf) + return NULL; + + vsnprintf(buf, length + 1, fmt, aq); + if (!utf8_check_string(buf, length)) { + jsonp_free(buf); + return NULL; + } + + return jsonp_stringn_nocheck_own(buf, length); +} + +json_t *json_sprintf(const char *fmt, ...) { + json_t *result; + va_list ap; + + va_start(ap, fmt); + result = json_vsprintf(fmt, ap); + va_end(ap); + + return result; +} + + +/*** integer ***/ + +json_t *json_integer(json_int_t value) +{ + json_integer_t *integer = jsonp_malloc(sizeof(json_integer_t)); + if(!integer) + return NULL; + json_init(&integer->json, JSON_INTEGER); + + integer->value = value; + return &integer->json; +} + +json_int_t json_integer_value(const json_t *json) +{ + if(!json_is_integer(json)) + return 0; + + return json_to_integer(json)->value; +} + +int json_integer_set(json_t *json, json_int_t value) +{ + if(!json_is_integer(json)) + return -1; + + json_to_integer(json)->value = value; + + return 0; +} + +static void json_delete_integer(json_integer_t *integer) +{ + jsonp_free(integer); +} + +static int json_integer_equal(const json_t *integer1, const json_t *integer2) +{ + return json_integer_value(integer1) == json_integer_value(integer2); +} + +static json_t *json_integer_copy(const json_t *integer) +{ + return json_integer(json_integer_value(integer)); +} + + +/*** real ***/ + +json_t *json_real(double value) +{ + json_real_t *real; + + if(isnan(value) || isinf(value)) + return NULL; + + real = jsonp_malloc(sizeof(json_real_t)); + if(!real) + return NULL; + json_init(&real->json, JSON_REAL); + + real->value = value; + return &real->json; +} + +double json_real_value(const json_t *json) +{ + if(!json_is_real(json)) + return 0; + + return json_to_real(json)->value; +} + +int json_real_set(json_t *json, double value) +{ + if(!json_is_real(json) || isnan(value) || isinf(value)) + return -1; + + json_to_real(json)->value = value; + + return 0; +} + +static void json_delete_real(json_real_t *real) +{ + jsonp_free(real); +} + +static int json_real_equal(const json_t *real1, const json_t *real2) +{ + return json_real_value(real1) == json_real_value(real2); +} + +static json_t *json_real_copy(const json_t *real) +{ + return json_real(json_real_value(real)); +} + + +/*** number ***/ + +double json_number_value(const json_t *json) +{ + if(json_is_integer(json)) + return (double)json_integer_value(json); + else if(json_is_real(json)) + return json_real_value(json); + else + return 0.0; +} + + +/*** simple values ***/ + +json_t *json_true(void) +{ + static json_t the_true = {JSON_TRUE, (size_t)-1}; + return &the_true; +} + + +json_t *json_false(void) +{ + static json_t the_false = {JSON_FALSE, (size_t)-1}; + return &the_false; +} + + +json_t *json_null(void) +{ + static json_t the_null = {JSON_NULL, (size_t)-1}; + return &the_null; +} + + +/*** deletion ***/ + +void json_delete(json_t *json) +{ + if (!json) + return; + + switch(json_typeof(json)) { + case JSON_OBJECT: + json_delete_object(json_to_object(json)); + break; + case JSON_ARRAY: + json_delete_array(json_to_array(json)); + break; + case JSON_STRING: + json_delete_string(json_to_string(json)); + break; + case JSON_INTEGER: + json_delete_integer(json_to_integer(json)); + break; + case JSON_REAL: + json_delete_real(json_to_real(json)); + break; + default: + return; + } + + /* json_delete is not called for true, false or null */ +} + + +/*** equality ***/ + +int json_equal(const json_t *json1, const json_t *json2) +{ + if(!json1 || !json2) + return 0; + + if(json_typeof(json1) != json_typeof(json2)) + return 0; + + /* this covers true, false and null as they are singletons */ + if(json1 == json2) + return 1; + + switch(json_typeof(json1)) { + case JSON_OBJECT: + return json_object_equal(json1, json2); + case JSON_ARRAY: + return json_array_equal(json1, json2); + case JSON_STRING: + return json_string_equal(json1, json2); + case JSON_INTEGER: + return json_integer_equal(json1, json2); + case JSON_REAL: + return json_real_equal(json1, json2); + default: + return 0; + } +} + + +/*** copying ***/ + +json_t *json_copy(json_t *json) +{ + if(!json) + return NULL; + + switch(json_typeof(json)) { + case JSON_OBJECT: + return json_object_copy(json); + case JSON_ARRAY: + return json_array_copy(json); + case JSON_STRING: + return json_string_copy(json); + case JSON_INTEGER: + return json_integer_copy(json); + case JSON_REAL: + return json_real_copy(json); + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + return json; + default: + return NULL; + } + + return NULL; +} + +json_t *json_deep_copy(const json_t *json) +{ + if(!json) + return NULL; + + switch(json_typeof(json)) { + case JSON_OBJECT: + return json_object_deep_copy(json); + case JSON_ARRAY: + return json_array_deep_copy(json); + /* for the rest of the types, deep copying doesn't differ from + shallow copying */ + case JSON_STRING: + return json_string_copy(json); + case JSON_INTEGER: + return json_integer_copy(json); + case JSON_REAL: + return json_real_copy(json); + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + return (json_t *)json; + default: + return NULL; + } + + return NULL; +} diff --git a/client/loclass/cipher.c b/client/loclass/cipher.c index a701da31..3b146b10 100644 --- a/client/loclass/cipher.c +++ b/client/loclass/cipher.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ @@ -114,9 +113,9 @@ uint8_t _select(bool x, bool y, uint8_t r) bool r6 = r >> 1 & 0x1; bool r7 = r & 0x1; - bool z0 = (r0 & r2) ^ (r1 & ~r3) ^ (r2 | r4); + bool z0 = (r0 & r2) ^ (r1 & !r3) ^ (r2 | r4); bool z1 = (r0 | r2) ^ ( r5 | r7) ^ r1 ^ r6 ^ x ^ y; - bool z2 = (r3 & ~r5) ^ (r4 & r6 ) ^ r7 ^ x; + bool z2 = (r3 & !r5) ^ (r4 & r6 ) ^ r7 ^ x; // The three bitz z0.. z1 are packed into a uint8_t: // 00000ZZZ diff --git a/client/loclass/cipher.h b/client/loclass/cipher.h index 7afdb524..632910a9 100644 --- a/client/loclass/cipher.h +++ b/client/loclass/cipher.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ diff --git a/client/loclass/cipherutils.c b/client/loclass/cipherutils.c index 9a8256bb..b5e365bf 100644 --- a/client/loclass/cipherutils.c +++ b/client/loclass/cipherutils.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #include diff --git a/client/loclass/cipherutils.h b/client/loclass/cipherutils.h index 3cdbf5fa..56867179 100644 --- a/client/loclass/cipherutils.h +++ b/client/loclass/cipherutils.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ diff --git a/client/loclass/elite_crack.c b/client/loclass/elite_crack.c index 777efe08..fe9bf7d1 100644 --- a/client/loclass/elite_crack.c +++ b/client/loclass/elite_crack.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #include diff --git a/client/loclass/elite_crack.h b/client/loclass/elite_crack.h index fb27355f..46791fb5 100644 --- a/client/loclass/elite_crack.h +++ b/client/loclass/elite_crack.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ diff --git a/client/loclass/fileutils.c b/client/loclass/fileutils.c index e5e5c5b0..90857e7a 100644 --- a/client/loclass/fileutils.c +++ b/client/loclass/fileutils.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #ifndef ON_DEVICE diff --git a/client/loclass/fileutils.h b/client/loclass/fileutils.h index 10720f76..79dbe566 100644 --- a/client/loclass/fileutils.h +++ b/client/loclass/fileutils.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #ifndef FILEUTILS_H diff --git a/client/loclass/ikeys.c b/client/loclass/ikeys.c index 8892b4a3..2a6a0010 100644 --- a/client/loclass/ikeys.c +++ b/client/loclass/ikeys.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ /** @@ -740,18 +739,16 @@ int readKeyFile(uint8_t key[8]) FILE *f; int retval = 1; f = fopen("iclass_key.bin", "rb"); - if (f) - { - if(fread(key, sizeof(uint8_t), 8, f) == 1) - { - retval = 0; - } - fclose(f); + if (!f) + return retval; + + if (fread(key, sizeof(uint8_t), 8, f) == 8) { + retval = 0; } + fclose(f); return retval; } - int doKeyTests(uint8_t debuglevel) { debug_print = debuglevel; diff --git a/client/loclass/ikeys.h b/client/loclass/ikeys.h index 13096194..5b44d179 100644 --- a/client/loclass/ikeys.h +++ b/client/loclass/ikeys.h @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ #ifndef IKEYS_H diff --git a/client/loclass/main.c b/client/loclass/main.c index 95112f7d..fa9886ee 100644 --- a/client/loclass/main.c +++ b/client/loclass/main.c @@ -22,7 +22,7 @@ * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. + * by the Free Software Foundation, or, at your option, any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,7 +33,6 @@ * along with loclass. If not, see . * * - * ****************************************************************************/ diff --git a/client/lualibs/read14a.lua b/client/lualibs/read14a.lua index 943a1722..60fc0e68 100644 --- a/client/lualibs/read14a.lua +++ b/client/lualibs/read14a.lua @@ -2,7 +2,7 @@ This is a library to read 14443a tags. It can be used something like this local reader = require('read14a') - result, err = reader.read1443a() + result, err = reader.read14443a() if not result then print(err) return @@ -22,10 +22,11 @@ local ISO14A_COMMAND = { ISO14A_APPEND_CRC = 0x20, ISO14A_SET_TIMEOUT = 0x40, ISO14A_NO_SELECT = 0x80, - ISO14A_TOPAZMODE = 0x100 + ISO14A_TOPAZMODE = 0x100, + ISO14A_NO_RATS = 0x200 } -local ISO14443a_TYPES = {} +local ISO14443a_TYPES = {} ISO14443a_TYPES[0x00] = "NXP MIFARE Ultralight | Ultralight C" ISO14443a_TYPES[0x01] = "NXP MIFARE TNP3xxx Activision Game Appliance" ISO14443a_TYPES[0x04] = "NXP MIFARE (various !DESFire !DESFire EV1)" @@ -42,14 +43,14 @@ ISO14443a_TYPES[0x88] = "Infineon MIFARE CLASSIC 1K" ISO14443a_TYPES[0x98] = "Gemplus MPCOS" -local function tostring_1443a(sak) +local function tostring_14443a(sak) return ISO14443a_TYPES[sak] or ("Unknown (SAK=%x)"):format(sak) end -local function parse1443a(data) +local function parse14443a(data) --[[ - Based on this struct : + Based on this struct : typedef struct { byte_t uid[10]; @@ -65,14 +66,14 @@ local function parse1443a(data) local count,uid,uidlen, atqa, sak, ats_len, ats= bin.unpack('H10CH2CC',data) uid = uid:sub(1,2*uidlen) --print("uid, atqa, sak: ",uid, atqa, sak) - --print("TYPE: ", tostring_1443a(sak)) - return { uid = uid, atqa = atqa, sak = sak, name = tostring_1443a(sak)} + --print("TYPE: ", tostring_14443a(sak)) + return { uid = uid, atqa = atqa, sak = sak, name = tostring_14443a(sak)} end --- Sends a USBpacket to the device -- @param command - the usb packet to send --- @param ignoreresponse - if set to true, we don't read the device answer packet --- which is usually recipe for fail. If not sent, the host will wait 2s for a +-- @param ignoreresponse - if set to true, we don't read the device answer packet +-- which is usually recipe for fail. If not sent, the host will wait 2s for a -- response of type CMD_ACK -- @return packet,nil if successfull -- nil, errormessage if unsuccessfull @@ -92,37 +93,41 @@ end -- This function does a connect and retrieves som einfo -- @param dont_disconnect - if true, does not disable the field +-- @param no_rats - if true, skips ISO14443-4 select (RATS) -- @return if successfull: an table containing card info -- @return if unsuccessfull : nil, error -local function read14443a(dont_disconnect) +local function read14443a(dont_disconnect, no_rats) local command, result, info, err, data - command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, + command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, arg1 = ISO14A_COMMAND.ISO14A_CONNECT} if dont_disconnect then command.arg1 = command.arg1 + ISO14A_COMMAND.ISO14A_NO_DISCONNECT end + if no_rats then + command.arg1 = command.arg1 + ISO14A_COMMAND.ISO14A_NO_RATS + end local result,err = sendToDevice(command) if result then local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL',result) - if arg0 == 0 then + if arg0 == 0 then return nil, "iso14443a card select failed" end data = string.sub(result,count) - info, err = parse1443a(data) + info, err = parse14443a(data) else err ="No response from card" end - if err then - print(err) + if err then + print(err) return nil, err end return info end --- --- Waits for a mifare card to be placed within the vicinity of the reader. +-- Waits for a mifare card to be placed within the vicinity of the reader. -- @return if successfull: an table containing card info -- @return if unsuccessfull : nil, error local function waitFor14443a() @@ -134,14 +139,14 @@ local function waitFor14443a() end return nil, "Aborted by user" end + local library = { - - read1443a = read14443a, - read = read14443a, + read14443a = read14443a, + read = read14443a, waitFor14443a = waitFor14443a, - parse1443a = parse1443a, + parse14443a = parse14443a, sendToDevice = sendToDevice, ISO14A_COMMAND = ISO14A_COMMAND, } -return library \ No newline at end of file +return library diff --git a/client/mifare4.c b/client/mifare4.c new file mode 100644 index 00000000..866e854f --- /dev/null +++ b/client/mifare4.c @@ -0,0 +1,265 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 Merlok +// Copyright (C) 2018 drHatson +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// iso14443-4 mifare commands +//----------------------------------------------------------------------------- + +#include "mifare4.h" +#include +#include +#include "cmdhf14a.h" +#include "util.h" +#include "ui.h" +#include "polarssl/libpcrypto.h" + +int CalculateEncIVCommand(mf4Session *session, uint8_t *iv, bool verbose) { + memcpy(&iv[0], session->TI, 4); + memcpy(&iv[4], &session->R_Ctr, 2); + memcpy(&iv[6], &session->W_Ctr, 2); + memcpy(&iv[8], &session->R_Ctr, 2); + memcpy(&iv[10], &session->W_Ctr, 2); + memcpy(&iv[12], &session->R_Ctr, 2); + memcpy(&iv[14], &session->W_Ctr, 2); + + return 0; +} + +int CalculateEncIVResponse(mf4Session *session, uint8_t *iv, bool verbose) { + memcpy(&iv[0], &session->R_Ctr, 2); + memcpy(&iv[2], &session->W_Ctr, 2); + memcpy(&iv[4], &session->R_Ctr, 2); + memcpy(&iv[6], &session->W_Ctr, 2); + memcpy(&iv[8], &session->R_Ctr, 2); + memcpy(&iv[10], &session->W_Ctr, 2); + memcpy(&iv[12], session->TI, 4); + + return 0; +} + + +int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose) { + if (!session || !session->Authenticated || !mac || !data || !datalen || datalen < 1) + return 1; + + memset(mac, 0x00, 8); + + uint16_t ctr = session->R_Ctr; + switch(mtype) { + case mtypWriteCmd: + case mtypWriteResp: + ctr = session->W_Ctr; + break; + case mtypReadCmd: + case mtypReadResp: + break; + } + + uint8_t macdata[2049] = {data[0], (ctr & 0xFF), (ctr >> 8), 0}; + int macdatalen = datalen; + memcpy(&macdata[3], session->TI, 4); + + switch(mtype) { + case mtypReadCmd: + memcpy(&macdata[7], &data[1], datalen - 1); + macdatalen = datalen + 6; + break; + case mtypReadResp: + macdata[7] = blockNum; + macdata[8] = 0; + macdata[9] = blockCount; + memcpy(&macdata[10], &data[1], datalen - 1); + macdatalen = datalen + 9; + break; + case mtypWriteCmd: + memcpy(&macdata[7], &data[1], datalen - 1); + macdatalen = datalen + 6; + break; + case mtypWriteResp: + macdatalen = 1 + 6; + break; + } + + if (verbose) + PrintAndLog("MAC data[%d]: %s", macdatalen, sprint_hex(macdata, macdatalen)); + + return aes_cmac8(NULL, session->Kmac, macdata, mac, macdatalen); +} + +int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose) { + uint8_t data[257] = {0}; + int datalen = 0; + + 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 (session) + session->Authenticated = false; + + uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; + int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("ERROR exchande raw error: %d", res); + DropField(); + return 2; + } + + if (verbose) + PrintAndLog("phase2: %s", sprint_hex(cmd2, 33)); + + res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen); + if (res) { + PrintAndLog("ERROR exchande raw error: %d", res); + DropField(); + return 4; + } + + if (verbose) + PrintAndLog("Authenticated = true; + session->R_Ctr = 0; + session->W_Ctr = 0; + session->KeyNum = keyn[1] + (keyn[0] << 8); + memmove(session->RndA, RndA, 16); + memmove(session->RndB, RndB, 16); + memmove(session->Key, key, 16); + memmove(session->TI, raw, 4); + memmove(session->PICCap2, &raw[20], 6); + memmove(session->PCDCap2, &raw[26], 6); + memmove(session->Kenc, kenc, 16); + memmove(session->Kmac, kmac, 16); + } + + PrintAndLog("Authentication OK"); + + return 0; +} + +// Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards), +// plus evtl. 8 sectors with 16 blocks each (4k cards) +uint8_t mfNumBlocksPerSector(uint8_t sectorNo) { + if (sectorNo < 32) + return 4; + else + return 16; +} + +uint8_t mfFirstBlockOfSector(uint8_t sectorNo) { + if (sectorNo < 32) + return sectorNo * 4; + else + return 32 * 4 + (sectorNo - 32) * 16; +} + +uint8_t mfSectorTrailer(uint8_t blockNo) { + if (blockNo < 32*4) { + return (blockNo | 0x03); + } else { + return (blockNo | 0x0f); + } +} + +bool mfIsSectorTrailer(uint8_t blockNo) { + return (blockNo == mfSectorTrailer(blockNo)); +} + +uint8_t mfSectorNum(uint8_t blockNo) { + if (blockNo < 32 * 4) + return blockNo / 4; + else + return 32 + (blockNo - 32 * 4) / 16; + +} diff --git a/client/mifare4.h b/client/mifare4.h new file mode 100644 index 00000000..2453797b --- /dev/null +++ b/client/mifare4.h @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 Merlok +// Copyright (C) 2018 drHatson +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// iso14443-4 mifare commands +//----------------------------------------------------------------------------- + +#ifndef MIFARE4_H +#define MIFARE4_H + +#include +#include +#include + +typedef struct { + bool Authenticated; + uint8_t Key[16]; + uint16_t KeyNum; + uint8_t RndA[16]; + uint8_t RndB[16]; + uint8_t TI[4]; + uint8_t PICCap2[6]; + uint8_t PCDCap2[6]; + uint8_t Kenc[16]; + uint8_t Kmac[16]; + uint16_t R_Ctr; + uint16_t W_Ctr; +}mf4Session; + +typedef enum { + mtypReadCmd, + mtypReadResp, + mtypWriteCmd, + mtypWriteResp, +} MACType_t; + +extern int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose); +extern int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose); + +extern uint8_t mfNumBlocksPerSector(uint8_t sectorNo); +extern uint8_t mfFirstBlockOfSector(uint8_t sectorNo); +extern uint8_t mfSectorTrailer(uint8_t blockNo); +extern bool mfIsSectorTrailer(uint8_t blockNo); +extern uint8_t mfSectorNum(uint8_t blockNo); + + +#endif // mifare4.h diff --git a/client/mifaredefault.h b/client/mifaredefault.h new file mode 100644 index 00000000..78231c86 --- /dev/null +++ b/client/mifaredefault.h @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Mifare default constants +//----------------------------------------------------------------------------- + +#ifndef MIFAREDEFAULT_H__ +#define MIFAREDEFAULT_H__ + +#include + +#define MifareDefaultKeysSize sizeof(MifareDefaultKeys) / sizeof(uint64_t) + +static const uint64_t MifareDefaultKeys[] = +{ + 0xffffffffffff, // Default key (first key used by program if no user defined key) + 0x000000000000, // Blank key + 0xa0a1a2a3a4a5, // NFCForum MAD key + 0xb0b1b2b3b4b5, + 0xaabbccddeeff, + 0x1a2b3c4d5e6f, + 0x123456789abc, + 0x010203040506, + 0x123456abcdef, + 0xabcdef123456, + 0x4d3a99c351dd, + 0x1a982c7e459a, + 0xd3f7d3f7d3f7, + 0x714c5c886e97, + 0x587ee5f9350f, + 0xa0478cc39091, + 0x533cb6c723f6, + 0x8fd0a4f256e9 +}; + +#endif diff --git a/client/mifarehost.c b/client/mifarehost.c index 92ffbd4e..9be04b4d 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -16,10 +16,11 @@ #include #include "crapto1/crapto1.h" -#include "proxmark3.h" +#include "comms.h" #include "usb_cmd.h" #include "cmdmain.h" #include "ui.h" +#include "parity.h" #include "util.h" #include "iso14443crc.h" @@ -73,13 +74,12 @@ static uint32_t intersection(uint64_t *list1, uint64_t *list2) // Darkside attack (hf mf mifare) -static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint64_t par_info, uint64_t ks_info, uint64_t **keys) { +static 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) { struct Crypto1State *states; - uint32_t i, pos, rr; //nr_diff; + uint32_t i, pos; uint8_t bt, ks3x[8], par[8][8]; uint64_t key_recovered; - static uint64_t *keylist; - rr = 0; + uint64_t *keylist; // Reset the last three significant bits of the reader nonce nr &= 0xffffff1f; @@ -92,7 +92,7 @@ static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint64_t par_i } } - states = lfsr_common_prefix(nr, rr, ks3x, par, (par_info == 0)); + states = lfsr_common_prefix(nr, ar, ks3x, par, (par_info == 0)); if (states == NULL) { *keys = NULL; @@ -116,7 +116,7 @@ static uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint64_t par_i int mfDarkside(uint64_t *key) { uint32_t uid = 0; - uint32_t nt = 0, nr = 0; + uint32_t nt = 0, nr = 0, ar = 0; uint64_t par_list = 0, ks_list = 0; uint64_t *keylist = NULL, *last_keylist = NULL; uint32_t keycount = 0; @@ -159,18 +159,18 @@ int mfDarkside(uint64_t *key) nt = (uint32_t)bytes_to_num(resp.d.asBytes + 4, 4); par_list = bytes_to_num(resp.d.asBytes + 8, 8); ks_list = bytes_to_num(resp.d.asBytes + 16, 8); - nr = bytes_to_num(resp.d.asBytes + 24, 4); + nr = (uint32_t)bytes_to_num(resp.d.asBytes + 24, 4); + ar = (uint32_t)bytes_to_num(resp.d.asBytes + 28, 4); break; } } if (par_list == 0 && c.arg[0] == true) { PrintAndLog("Parity is all zero. Most likely this card sends NACK on every failed authentication."); - PrintAndLog("Attack will take a few seconds longer because we need two consecutive successful runs."); } c.arg[0] = false; - keycount = nonce2key(uid, nt, nr, par_list, ks_list, &keylist); + keycount = nonce2key(uid, nt, nr, ar, par_list, ks_list, &keylist); if (keycount == 0) { PrintAndLog("Key not found (lfsr_common_prefix list is null). Nt=%08x", nt); @@ -178,12 +178,14 @@ int mfDarkside(uint64_t *key) continue; } - qsort(keylist, keycount, sizeof(*keylist), compare_uint64); - keycount = intersection(last_keylist, keylist); - if (keycount == 0) { - free(last_keylist); - last_keylist = keylist; - continue; + if (par_list == 0) { + qsort(keylist, keycount, sizeof(*keylist), compare_uint64); + keycount = intersection(last_keylist, keylist); + if (keycount == 0) { + free(last_keylist); + last_keylist = keylist; + continue; + } } if (keycount > 1) { @@ -198,10 +200,10 @@ int mfDarkside(uint64_t *key) for (int i = 0; i < keycount; i += max_keys) { int size = keycount - i > max_keys ? max_keys : keycount - i; for (int j = 0; j < size; j++) { - if (last_keylist == NULL) { - num_to_bytes(keylist[i*max_keys + j], 6, keyBlock); + if (par_list == 0) { + num_to_bytes(last_keylist[i*max_keys + j], 6, keyBlock+(j*6)); } else { - num_to_bytes(last_keylist[i*max_keys + j], 6, keyBlock); + num_to_bytes(keylist[i*max_keys + j], 6, keyBlock+(j*6)); } } if (!mfCheckKeys(0, 0, false, size, keyBlock, key)) { @@ -228,17 +230,46 @@ int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t key *key = -1; - UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType&0xff)<<8)), clear_trace, keycnt}}; + UsbCommand c = {CMD_MIFARE_CHKKEYS, {((blockNo & 0xff) | ((keyType & 0xff) << 8)), clear_trace, keycnt}}; memcpy(c.d.asBytes, keyBlock, 6 * keycnt); SendCommand(&c); UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK,&resp,3000)) return 1; + if (!WaitForResponseTimeout(CMD_ACK,&resp,3000)) return 1; if ((resp.arg[0] & 0xff) != 0x01) return 2; *key = bytes_to_num(resp.d.asBytes, 6); return 0; } +int mfCheckKeysSec(uint8_t sectorCnt, uint8_t keyType, uint8_t timeout14a, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, sector_t * e_sector){ + + uint8_t keyPtr = 0; + + if (e_sector == NULL) + return -1; + + UsbCommand c = {CMD_MIFARE_CHKKEYS, {((sectorCnt & 0xff) | ((keyType & 0xff) << 8)), (clear_trace | 0x02)|((timeout14a & 0xff) << 8), keycnt}}; + memcpy(c.d.asBytes, keyBlock, 6 * keycnt); + SendCommand(&c); + + UsbCommand resp; + if (!WaitForResponseTimeoutW(CMD_ACK, &resp, MAX(3000, 1000 + 13 * sectorCnt * keycnt * (keyType == 2 ? 2 : 1)), false)) return 1; // timeout: 13 ms / fail auth + if ((resp.arg[0] & 0xff) != 0x01) return 2; + + bool foundAKey = false; + for(int sec = 0; sec < sectorCnt; sec++){ + for(int keyAB = 0; keyAB < 2; keyAB++){ + keyPtr = *(resp.d.asBytes + keyAB * 40 + sec); + if (keyPtr){ + e_sector[sec].foundKey[keyAB] = true; + e_sector[sec].Key[keyAB] = bytes_to_num(keyBlock + (keyPtr - 1) * 6, 6); + foundAKey = true; + } + } + } + return foundAKey ? 0 : 3; +} + // Compare 16 Bits out of cryptostate int Compare16Bits(const void * a, const void * b) { if ((*(uint64_t*)b & 0x00ff000000ff0000) == (*(uint64_t*)a & 0x00ff000000ff0000)) return 0; @@ -266,7 +297,13 @@ typedef // wrapper function for multi-threaded lfsr_recovery32 -void* nested_worker_thread(void *arg) +void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +*nested_worker_thread(void *arg) { struct Crypto1State *p1; StateList_t *statelist = arg; @@ -280,6 +317,7 @@ void* nested_worker_thread(void *arg) 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) { uint16_t i; @@ -290,7 +328,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, struct Crypto1State *p1, *p2, *p3, *p4; // flush queue - WaitForResponseTimeout(CMD_ACK, NULL, 100); + (void)WaitForResponseTimeout(CMD_ACK,NULL,100); UsbCommand c = {CMD_MIFARE_NESTED, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, calibrate}}; memcpy(c.d.asBytes, key, 6); @@ -396,7 +434,8 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { UsbCommand c = {CMD_MIFARE_EML_MEMGET, {blockNum, blocksCount, 0}}; SendCommand(&c); - UsbCommand resp; + + UsbCommand resp; if (!WaitForResponseTimeout(CMD_ACK,&resp,1500)) return 1; memcpy(data, resp.d.asBytes, blocksCount * 16); return 0; @@ -416,6 +455,7 @@ int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params) { UsbCommand c = {CMD_MIFARE_CGETBLOCK, {params, 0, blockNo}}; SendCommand(&c); + UsbCommand resp; if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { isOK = resp.arg[0] & 0xff; @@ -434,8 +474,9 @@ int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWipe, uin UsbCommand c = {CMD_MIFARE_CSETBLOCK, {wantWipe, params & (0xFE | (uid == NULL ? 0:1)), blockNo}}; memcpy(c.d.asBytes, data, 16); SendCommand(&c); + UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { isOK = resp.arg[0] & 0xff; if (uid != NULL) memcpy(uid, resp.d.asBytes, 4); @@ -445,25 +486,40 @@ int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWipe, uin PrintAndLog("Command execute timeout"); return 1; } + return 0; } -int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool wantWipe) { +int mfCWipe(uint32_t numSectors, bool gen1b, bool wantWipe, bool wantFill) { + uint8_t isOK = 0; + uint8_t cmdParams = wantWipe + wantFill * 0x02 + gen1b * 0x04; + UsbCommand c = {CMD_MIFARE_CWIPE, {numSectors, cmdParams, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + isOK = resp.arg[0] & 0xff; + + return isOK; +} + +int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID) { uint8_t oldblock0[16] = {0x00}; uint8_t block0[16] = {0x00}; - int old, gen = 0; + int gen = 0, res; gen = mfCIdentify(); + /* generation 1a magic card by default */ + uint8_t cmdParams = CSETBLOCK_SINGLE_OPER; if (gen == 2) { /* generation 1b magic card */ - old = mfCGetBlock(0, oldblock0, CSETBLOCK_SINGLE_OPER | CSETBLOCK_MAGIC_1B); - } else { - /* generation 1a magic card by default */ - old = mfCGetBlock(0, oldblock0, CSETBLOCK_SINGLE_OPER); + cmdParams = CSETBLOCK_SINGLE_OPER | CSETBLOCK_MAGIC_1B; } + + res = mfCGetBlock(0, oldblock0, cmdParams); - if (old == 0) { + if (res == 0) { memcpy(block0, oldblock0, 16); PrintAndLog("old block 0: %s", sprint_hex(block0,16)); } else { @@ -474,25 +530,42 @@ int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool w // UID memcpy(block0, uid, 4); // Mifare UID BCC - block0[4] = block0[0]^block0[1]^block0[2]^block0[3]; + block0[4] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3]; // mifare classic SAK(byte 5) and ATQA(byte 6 and 7, reversed) - if (sak!=NULL) - block0[5]=sak[0]; - if (atqa!=NULL) { - block0[6]=atqa[1]; - block0[7]=atqa[0]; + if (sak != NULL) + block0[5] = sak[0]; + if (atqa != NULL) { + block0[6] = atqa[1]; + block0[7] = atqa[0]; } - PrintAndLog("new block 0: %s", sprint_hex(block0,16)); + PrintAndLog("new block 0: %s", sprint_hex(block0, 16)); - if (gen == 2) { - /* generation 1b magic card */ - return mfCSetBlock(0, block0, oldUID, wantWipe, CSETBLOCK_SINGLE_OPER | CSETBLOCK_MAGIC_1B); - } else { - /* generation 1a magic card by default */ - return mfCSetBlock(0, block0, oldUID, wantWipe, CSETBLOCK_SINGLE_OPER); + res = mfCSetBlock(0, block0, oldUID, false, cmdParams); + if (res) { + PrintAndLog("Can't set block 0. Error: %d", res); + return res; } + + return 0; } +int mfCIdentify() { + UsbCommand c = {CMD_MIFARE_CIDENT, {0, 0, 0}}; + SendCommand(&c); + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + uint8_t isGeneration = resp.arg[0] & 0xff; + switch( isGeneration ){ + case 1: PrintAndLog("Chinese magic backdoor commands (GEN 1a) detected"); break; + case 2: PrintAndLog("Chinese magic backdoor command (GEN 1b) detected"); break; + default: PrintAndLog("No chinese magic backdoor command detected"); break; + } + + return (int) isGeneration; +} + + // SNIFFER // constants @@ -510,14 +583,19 @@ struct Crypto1State *traceCrypto1 = NULL; struct Crypto1State *revstate; uint64_t lfsr; +uint64_t ui64Key; uint32_t ks2; uint32_t ks3; -uint32_t uid; // serial number -uint32_t nt; // tag challenge -uint32_t nr_enc; // encrypted reader challenge -uint32_t ar_enc; // encrypted reader response -uint32_t at_enc; // encrypted tag response +uint32_t uid; // serial number +uint32_t nt; // tag challenge +uint32_t nt_enc; // encrypted tag challenge +uint8_t nt_enc_par; // encrypted tag challenge parity +uint32_t nr_enc; // encrypted reader challenge +uint32_t ar_enc; // encrypted reader response +uint8_t ar_enc_par; // encrypted reader response parity +uint32_t at_enc; // encrypted tag response +uint8_t at_enc_par; // encrypted tag response parity int isTraceCardEmpty(void) { return ((traceCard[0] == 0) && (traceCard[1] == 0) && (traceCard[2] == 0) && (traceCard[3] == 0)); @@ -531,7 +609,7 @@ int isBlockEmpty(int blockN) { } int isBlockTrailer(int blockN) { - return ((blockN & 0x03) == 0x03); + return ((blockN & 0x03) == 0x03); } int saveTraceCard(void) { @@ -545,7 +623,8 @@ int saveTraceCard(void) { for (int i = 0; i < 64; i++) { // blocks for (int j = 0; j < 16; j++) // bytes fprintf(f, "%02x", *(traceCard + i * 16 + j)); - fprintf(f,"\n"); + if (i < 63) + fprintf(f,"\n"); } fclose(f); return 0; @@ -593,6 +672,7 @@ int loadTraceCard(uint8_t *tuid) { blockNum++; } fclose(f); + return 0; } @@ -613,6 +693,7 @@ int mfTraceInit(uint8_t *tuid, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFil uid = bytes_to_num(tuid + 3, 4); traceState = TRACE_IDLE; + return 0; } @@ -633,8 +714,36 @@ void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool i return; } +bool NTParityCheck(uint32_t ntx) { + if ( + (oddparity8(ntx >> 8 & 0xff) ^ (ntx & 0x01) ^ ((nt_enc_par >> 5) & 0x01) ^ (nt_enc & 0x01)) || + (oddparity8(ntx >> 16 & 0xff) ^ (ntx >> 8 & 0x01) ^ ((nt_enc_par >> 6) & 0x01) ^ (nt_enc >> 8 & 0x01)) || + (oddparity8(ntx >> 24 & 0xff) ^ (ntx >> 16 & 0x01) ^ ((nt_enc_par >> 7) & 0x01) ^ (nt_enc >> 16 & 0x01)) + ) + return false; + + uint32_t ar = prng_successor(ntx, 64); + if ( + (oddparity8(ar >> 8 & 0xff) ^ (ar & 0x01) ^ ((ar_enc_par >> 5) & 0x01) ^ (ar_enc & 0x01)) || + (oddparity8(ar >> 16 & 0xff) ^ (ar >> 8 & 0x01) ^ ((ar_enc_par >> 6) & 0x01) ^ (ar_enc >> 8 & 0x01)) || + (oddparity8(ar >> 24 & 0xff) ^ (ar >> 16 & 0x01) ^ ((ar_enc_par >> 7) & 0x01) ^ (ar_enc >> 16 & 0x01)) + ) + return false; -int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { + uint32_t at = prng_successor(ntx, 96); + if ( + (oddparity8(ar & 0xff) ^ (at >> 24 & 0x01) ^ ((ar_enc_par >> 4) & 0x01) ^ (at_enc >> 24 & 0x01)) || + (oddparity8(at >> 8 & 0xff) ^ (at & 0x01) ^ ((at_enc_par >> 5) & 0x01) ^ (at_enc & 0x01)) || + (oddparity8(at >> 16 & 0xff) ^ (at >> 8 & 0x01) ^ ((at_enc_par >> 6) & 0x01) ^ (at_enc >> 8 & 0x01)) || + (oddparity8(at >> 24 & 0xff) ^ (at >> 16 & 0x01) ^ ((at_enc_par >> 7) & 0x01) ^ (at_enc >> 16 & 0x01)) + ) + return false; + + return true; +} + + +int mfTraceDecode(uint8_t *data_src, int len, uint8_t parity, bool wantSaveToEmlFile) { uint8_t data[64]; if (traceState == TRACE_ERROR) return 1; @@ -646,7 +755,9 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { memcpy(data, data_src, len); if ((traceCrypto1) && ((traceState == TRACE_IDLE) || (traceState > TRACE_AUTH_OK))) { mf_crypto1_decrypt(traceCrypto1, data, len, 0); - PrintAndLog("dec> %s", sprint_hex(data, len)); + uint8_t parity[16]; + oddparitybuf(data, len, parity); + PrintAndLog("dec> %s [%s]", sprint_hex(data, len), printBitsPar(parity, len)); AddLogHex(logHexFileName, "dec> ", data, len); } @@ -735,7 +846,12 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { case TRACE_AUTH1: if (len == 4) { traceState = TRACE_AUTH2; - nt = bytes_to_num(data, 4); + if (!traceCrypto1) { + nt = bytes_to_num(data, 4); + } else { + nt_enc = bytes_to_num(data, 4); + nt_enc_par = parity; + } return 0; } else { traceState = TRACE_ERROR; @@ -749,6 +865,7 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { nr_enc = bytes_to_num(data, 4); ar_enc = bytes_to_num(data + 4, 4); + ar_enc_par = parity << 4; return 0; } else { traceState = TRACE_ERROR; @@ -761,19 +878,90 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { traceState = TRACE_IDLE; at_enc = bytes_to_num(data, 4); + at_enc_par = parity; + if (!traceCrypto1) { - // decode key here) - ks2 = ar_enc ^ prng_successor(nt, 64); - ks3 = at_enc ^ prng_successor(nt, 96); - revstate = lfsr_recovery64(ks2, ks3); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, 0, 0); - lfsr_rollback_word(revstate, nr_enc, 1); - lfsr_rollback_word(revstate, uid ^ nt, 0); + // decode key here) + ks2 = ar_enc ^ prng_successor(nt, 64); + ks3 = at_enc ^ prng_successor(nt, 96); + revstate = lfsr_recovery64(ks2, ks3); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, nr_enc, 1); + lfsr_rollback_word(revstate, uid ^ nt, 0); - crypto1_get_lfsr(revstate, &lfsr); - printf("key> %x%x\n", (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF)); - AddLogUint64(logHexFileName, "key> ", lfsr); + crypto1_get_lfsr(revstate, &lfsr); + crypto1_destroy(revstate); + ui64Key = lfsr; + printf("key> probable key:%x%x Prng:%s ks2:%08x ks3:%08x\n", + (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), + validate_prng_nonce(nt) ? "WEAK": "HARDEND", + ks2, + ks3); + AddLogUint64(logHexFileName, "key> ", lfsr); + } else { + if (validate_prng_nonce(nt)) { + struct Crypto1State *pcs; + pcs = crypto1_create(ui64Key); + uint32_t nt1 = crypto1_word(pcs, nt_enc ^ uid, 1) ^ nt_enc; + uint32_t ar = prng_successor(nt1, 64); + uint32_t at = prng_successor(nt1, 96); + printf("key> nested auth uid: %08x nt: %08x nt_parity: %s ar: %08x at: %08x\n", uid, nt1, printBitsPar(&nt_enc_par, 4), ar, at); + uint32_t nr1 = crypto1_word(pcs, nr_enc, 1) ^ nr_enc; + uint32_t ar1 = crypto1_word(pcs, 0, 0) ^ ar_enc; + uint32_t at1 = crypto1_word(pcs, 0, 0) ^ at_enc; + crypto1_destroy(pcs); + printf("key> the same key test. nr1: %08x ar1: %08x at1: %08x \n", nr1, ar1, at1); + + if (NTParityCheck(nt1)) + printf("key> the same key test OK. key=%x%x\n", (unsigned int)((ui64Key & 0xFFFFFFFF00000000) >> 32), (unsigned int)(ui64Key & 0xFFFFFFFF)); + else + printf("key> the same key test. check nt parity error.\n"); + + uint32_t ntc = prng_successor(nt, 90); + uint32_t ntx = 0; + int ntcnt = 0; + for (int i = 0; i < 16383; i++) { + ntc = prng_successor(ntc, 1); + if (NTParityCheck(ntc)){ + if (!ntcnt) + ntx = ntc; + ntcnt++; + } + } + if (ntcnt) + printf("key> nt candidate=%08x nonce distance=%d candidates count=%d\n", ntx, nonce_distance(nt, ntx), ntcnt); + else + printf("key> don't have any nt candidate( \n"); + + nt = ntx; + ks2 = ar_enc ^ prng_successor(ntx, 64); + ks3 = at_enc ^ prng_successor(ntx, 96); + + // decode key + revstate = lfsr_recovery64(ks2, ks3); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, 0, 0); + lfsr_rollback_word(revstate, nr_enc, 1); + lfsr_rollback_word(revstate, uid ^ nt, 0); + + crypto1_get_lfsr(revstate, &lfsr); + crypto1_destroy(revstate); + ui64Key = lfsr; + printf("key> probable key:%x%x ks2:%08x ks3:%08x\n", + (unsigned int)((lfsr & 0xFFFFFFFF00000000) >> 32), (unsigned int)(lfsr & 0xFFFFFFFF), + ks2, + ks3); + AddLogUint64(logHexFileName, "key> ", lfsr); + } else { + printf("key> hardnested not implemented!\n"); + + crypto1_destroy(traceCrypto1); + + // not implemented + traceState = TRACE_ERROR; + } + } int blockShift = ((traceCurBlock & 0xFC) + 3) * 16; if (isBlockEmpty((traceCurBlock & 0xFC) + 3)) memcpy(traceCard + blockShift + 6, trailerAccessBytes, 4); @@ -791,15 +979,6 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { // set cryptosystem state traceCrypto1 = lfsr_recovery64(ks2, ks3); - -// nt = crypto1_word(traceCrypto1, nt ^ uid, 1) ^ nt; - - /* traceCrypto1 = crypto1_create(lfsr); // key in lfsr - crypto1_word(traceCrypto1, nt ^ uid, 0); - crypto1_word(traceCrypto1, ar, 1); - crypto1_word(traceCrypto1, 0, 0); - crypto1_word(traceCrypto1, 0, 0);*/ - return 0; } else { traceState = TRACE_ERROR; @@ -815,6 +994,8 @@ int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile) { return 0; } +// DECODING + int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len){ /* uint32_t nt; // tag challenge @@ -835,48 +1016,72 @@ int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, return 0; } -int mfCIdentify() -{ - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; - SendCommand(&c); - UsbCommand resp; - WaitForResponse(CMD_ACK,&resp); +/** validate_prng_nonce + * Determine if nonce is deterministic. ie: Suspectable to Darkside attack. + * returns + * true = weak prng + * false = hardend prng + */ +bool validate_prng_nonce(uint32_t nonce) { + uint16_t *dist = 0; + uint16_t x, i; - iso14a_card_select_t card; - memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + dist = malloc(2 << 16); + if(!dist) + return -1; - uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision - - if(select_status != 0) { - uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 - c.arg[0] = ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT; - c.arg[1] = 2; - c.arg[2] = 0; - memcpy(c.d.asBytes, rats, 2); - SendCommand(&c); - WaitForResponse(CMD_ACK,&resp); + // init prng table: + for (x = i = 1; i; ++i) { + dist[(x & 0xff) << 8 | x >> 8] = i; + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; } - - c.cmd = CMD_MIFARE_CIDENT; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - WaitForResponse(CMD_ACK,&resp); - - uint8_t isGeneration = resp.arg[0] & 0xff; - switch( isGeneration ){ - case 1: PrintAndLog("Chinese magic backdoor commands (GEN 1a) detected"); break; - case 2: PrintAndLog("Chinese magic backdoor command (GEN 1b) detected"); break; - default: PrintAndLog("No chinese magic backdoor command detected"); break; - } - - // disconnect - c.cmd = CMD_READER_ISO_14443a; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - - return (int) isGeneration; + + uint32_t res = (65535 - dist[nonce >> 16] + dist[nonce & 0xffff]) % 65535; + + free(dist); + return (res == 16); +} + +/* Detect Tag Prng, +* function performs a partial AUTH, where it tries to authenticate against block0, key A, but only collects tag nonce. +* the tag nonce is check to see if it has a predictable PRNG. +* @returns +* TRUE if tag uses WEAK prng (ie Now the NACK bug also needs to be present for Darkside attack) +* FALSE is tag uses HARDEND prng (ie hardnested attack possible, with known key) +*/ +int DetectClassicPrng(void){ + + UsbCommand resp, respA; + uint8_t cmd[] = {0x60, 0x00}; // MIFARE_AUTH_KEYA + uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + + UsbCommand c = {CMD_READER_ISO_14443a, {flags, sizeof(cmd), 0}}; + memcpy(c.d.asBytes, cmd, sizeof(cmd)); + + clearCommandBuffer(); + SendCommand(&c); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLog("PRNG UID: Reply timeout."); + return -1; + } + + // if select tag failed. + if (resp.arg[0] == 0) { + PrintAndLog("PRNG error: selecting tag failed, can't detect prng."); + return -1; + } + + if (!WaitForResponseTimeout(CMD_ACK, &respA, 5000)) { + PrintAndLog("PRNG data: Reply timeout."); + return -1; + } + + // check respA + if (respA.arg[0] != 4) { + PrintAndLog("PRNG data error: Wrong length: %d", respA.arg[0]); + return -1; + } + + uint32_t nonce = bytes_to_num(respA.d.asBytes, respA.arg[0]); + return validate_prng_nonce(nonce); } diff --git a/client/mifarehost.h b/client/mifarehost.h index 7f9a2b45..6a37fef1 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -1,4 +1,4 @@ -// Merlok, 2011 +// Merlok, 2011, 2017 // people from mifare@nethemba.com, 2010 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -13,7 +13,13 @@ #include #include -#include "data.h" +#include "crapto1/crapto1.h" +#include "util.h" + +// defaults +// timeout in units. (ms * 106)/10 or us*0.0106 +// 5 == 500us +#define MF_CHKKEYS_DEFTIMEOUT 5 // mfCSetBlock work flags #define CSETBLOCK_UID 0x01 @@ -24,21 +30,28 @@ #define CSETBLOCK_SINGLE_OPER 0x1F #define CSETBLOCK_MAGIC_1B 0x40 +typedef struct { + uint64_t Key[2]; + int foundKey[2]; +} sector_t; + extern char logHexFileName[FILE_PATH_SIZE]; extern int mfDarkside(uint64_t *key); extern int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *ResultKeys, bool calibrate); extern int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key); +extern int mfCheckKeysSec(uint8_t sectorCnt, uint8_t keyType, uint8_t timeout14a, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, sector_t * e_sector); extern int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); extern int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); -extern int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, bool wantWipe); +extern int mfCWipe(uint32_t numSectors, bool gen1b, bool wantWipe, bool wantFill); +extern int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID); extern int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, bool wantWipe, uint8_t params); extern int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params); extern int mfTraceInit(uint8_t *tuid, uint8_t *atqa, uint8_t sak, bool wantSaveToEmlFile); -extern int mfTraceDecode(uint8_t *data_src, int len, bool wantSaveToEmlFile); +extern int mfTraceDecode(uint8_t *data_src, int len, uint8_t parity, bool wantSaveToEmlFile); extern int isTraceCardEmpty(void); extern int isBlockEmpty(int blockN); @@ -48,5 +61,8 @@ extern int saveTraceCard(void); extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); extern int mfCIdentify(); +extern int DetectClassicPrng(void); +extern bool validate_prng_nonce(uint32_t nonce); +extern void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted); #endif diff --git a/client/obj/reveng/.dummy b/client/obj/cliparser/.dummy similarity index 100% rename from client/obj/reveng/.dummy rename to client/obj/cliparser/.dummy diff --git a/client/obj/emv/.dummy b/client/obj/emv/.dummy new file mode 100644 index 00000000..e69de29b diff --git a/client/obj/emv/test/.dummy b/client/obj/emv/test/.dummy new file mode 100644 index 00000000..e69de29b diff --git a/client/pm3_binlib.c b/client/pm3_binlib.c index 5c1e3cfc..cffbca6c 100644 --- a/client/pm3_binlib.c +++ b/client/pm3_binlib.c @@ -107,7 +107,8 @@ static int l_unpack(lua_State *L) /** unpack(f,s, [init]) */ size_t len; const char *s=luaL_checklstring(L,2,&len); /* switched s and f */ const char *f=luaL_checkstring(L,1); - int i_read = luaL_optint(L,3,1)-1; + int i_read = luaL_optinteger(L,3,1)-1; + // int i_read = luaL_optint(L,3,1)-1; unsigned int i; if (i_read >= 0) { i = i_read; @@ -305,7 +306,7 @@ static int l_pack(lua_State *L) /** pack(f,...) */ sbyte = 0; odd = 0; } - } else if (isspace(a[ii])) { + } else if (isspace((unsigned char)a[ii])) { /* ignore */ } else { /* err ... ignore too*/ @@ -347,4 +348,3 @@ int set_bin_library (lua_State *L) { lua_pop(L, 1); return 1; } - diff --git a/client/proxgui.cpp b/client/proxgui.cpp index e7fdae6c..18749158 100644 --- a/client/proxgui.cpp +++ b/client/proxgui.cpp @@ -15,7 +15,7 @@ static ProxGuiQT *gui = NULL; static WorkerThread *main_loop_thread = NULL; -WorkerThread::WorkerThread(char *script_cmds_file, bool usb_present) : script_cmds_file(script_cmds_file), usb_present(usb_present) +WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, bool usb_present) : script_cmds_file(script_cmds_file), script_cmd(script_cmd), usb_present(usb_present) { } @@ -24,7 +24,7 @@ WorkerThread::~WorkerThread() } void WorkerThread::run() { - main_loop(script_cmds_file, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present); } extern "C" void ShowGraphWindow(void) @@ -56,32 +56,25 @@ extern "C" void MainGraphics(void) if (!gui) return; - main_loop_thread->start(); gui->MainLoop(); } -extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, bool usb_present) +extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present) { #ifdef Q_WS_X11 - bool useGUI = getenv("DISPLAY") != 0; -#else - bool useGUI = true; -#endif - if (!useGUI) + if (getenv("DISPLAY") == NULL) return; +#endif - gui = new ProxGuiQT(argc, argv); - main_loop_thread = new WorkerThread(script_cmds_file, usb_present); - QObject::connect(main_loop_thread, SIGNAL(finished()), main_loop_thread, SLOT(deleteLater())); - QObject::connect(main_loop_thread, SIGNAL(finished()), gui, SLOT(_Exit())); + main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present); + gui = new ProxGuiQT(argc, argv, main_loop_thread); } - extern "C" void ExitGraphics(void) { - if (!gui) - return; + if (!gui) + return; - gui->Exit(); - gui = NULL; + gui->Exit(); + gui = NULL; } diff --git a/client/proxgui.h b/client/proxgui.h index 6e261cb9..5cad6e3a 100644 --- a/client/proxgui.h +++ b/client/proxgui.h @@ -19,7 +19,7 @@ void ShowGraphWindow(void); void HideGraphWindow(void); void RepaintGraphWindow(void); void MainGraphics(void); -void InitGraphics(int argc, char **argv, char *script_cmds_file, bool usb_present); +void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present); void ExitGraphics(void); #define MAX_GRAPH_TRACE_LEN (40000*8) @@ -30,7 +30,6 @@ extern int s_Buff[MAX_GRAPH_TRACE_LEN]; extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; extern int CommandFinished; -extern int offline; extern bool GridLocked; //Operations defined in data_operations diff --git a/client/proxguiqt.cpp b/client/proxguiqt.cpp index 506b8138..30e4c8de 100644 --- a/client/proxguiqt.cpp +++ b/client/proxguiqt.cpp @@ -26,6 +26,10 @@ #include #include "proxgui.h" #include + +extern "C" { +#include "util_darwin.h" +} //#include bool g_useOverlays = false; @@ -60,7 +64,12 @@ void ProxGuiQT::_ShowGraphWindow(void) return; if (!plotwidget) + { +#if defined(__MACH__) && defined(__APPLE__) + makeFocusable(); +#endif plotwidget = new ProxWidget(); + } plotwidget->show(); } @@ -85,6 +94,17 @@ void ProxGuiQT::_Exit(void) { delete this; } +void ProxGuiQT::_StartProxmarkThread(void) { + if (!proxmarkThread) + return; + + // if thread finished delete self and delete application + QObject::connect(proxmarkThread, SIGNAL(finished()), proxmarkThread, SLOT(deleteLater())); + QObject::connect(proxmarkThread, SIGNAL(finished()), this, SLOT(_Exit())); + // start proxmark thread + proxmarkThread->start(); +} + void ProxGuiQT::MainLoop() { plotapp = new QApplication(argc, argv); @@ -94,11 +114,19 @@ void ProxGuiQT::MainLoop() connect(this, SIGNAL(HideGraphWindowSignal()), this, SLOT(_HideGraphWindow())); connect(this, SIGNAL(ExitSignal()), this, SLOT(_Exit())); + //start proxmark thread after starting event loop + QTimer::singleShot(200, this, SLOT(_StartProxmarkThread())); + +#if defined(__MACH__) && defined(__APPLE__) + //Prevent the terminal from loosing focus during launch by making the client unfocusable + makeUnfocusable(); +#endif + plotapp->exec(); } -ProxGuiQT::ProxGuiQT(int argc, char **argv) : plotapp(NULL), plotwidget(NULL), - argc(argc), argv(argv) +ProxGuiQT::ProxGuiQT(int argc, char **argv, WorkerThread *wthread) : plotapp(NULL), plotwidget(NULL), + argc(argc), argv(argv), proxmarkThread(wthread) { } @@ -167,8 +195,7 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) this->master = master; resize(800,500); - /** Setup the controller widget **/ - + // Setup the controller widget controlWidget = new QWidget(); opsController = new Ui::Form(); opsController->setupUi(controlWidget); @@ -190,23 +217,17 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) QObject::connect(opsController->horizontalSlider_dirthr_down, SIGNAL(valueChanged(int)), this, SLOT(vchange_dthr_down(int))); QObject::connect(opsController->horizontalSlider_askedge, SIGNAL(valueChanged(int)), this, SLOT(vchange_askedge(int))); - controlWidget->show(); - // Set up the plot widget, which does the actual plotting - plot = new Plot(this); - /* - QSlider* slider = new QSlider(Qt::Horizontal); - slider->setFocusPolicy(Qt::StrongFocus); - slider->setTickPosition(QSlider::TicksBothSides); - slider->setTickInterval(10); - slider->setSingleStep(1); - */ QVBoxLayout *layout = new QVBoxLayout; - //layout->addWidget(slider); layout->addWidget(plot); setLayout(layout); - //printf("Proxwidget Constructor just set layout\r\n"); + show(); // places the window on the screen. + + // Move controller widget below plot + controlWidget->move(x(),y()+frameSize().height()); + controlWidget->resize(size().width(), controlWidget->size().height()); + controlWidget->show(); } // not 100% sure what i need in this block @@ -255,6 +276,7 @@ int Plot::xCoordOf(int i, QRect r ) int Plot::yCoordOf(int v, QRect r, int maxVal) { int z = (r.bottom() - r.top())/2; + if ( maxVal == 0 ) maxVal++; return -(z * v) / maxVal + z; } @@ -565,6 +587,8 @@ Plot::Plot(QWidget *parent) : QWidget(parent), GraphStart(0), GraphPixelsPerPoin CursorBPos = 0; setWindowTitle(tr("Sliders")); + + master = parent; } void Plot::closeEvent(QCloseEvent *event) @@ -674,7 +698,7 @@ void Plot::keyPressEvent(QKeyEvent *event) break; case Qt::Key_Q: - this->hide(); + master->hide(); break; default: diff --git a/client/proxguiqt.h b/client/proxguiqt.h index 8a3b8cfc..5f7199fc 100644 --- a/client/proxguiqt.h +++ b/client/proxguiqt.h @@ -28,6 +28,7 @@ class Plot: public QWidget { private: + QWidget *master; int GraphStart; double GraphPixelsPerPoint; int CursorAPos; @@ -88,6 +89,18 @@ class ProxWidget : public QWidget void vchange_dthr_down(int v); }; +class WorkerThread : public QThread { + Q_OBJECT; + public: + WorkerThread(char*, char*, bool); + ~WorkerThread(); + void run(); + private: + char *script_cmds_file = NULL; + char *script_cmd = NULL; + bool usb_present; +}; + class ProxGuiQT : public QObject { Q_OBJECT; @@ -97,10 +110,10 @@ class ProxGuiQT : public QObject ProxWidget *plotwidget; int argc; char **argv; - void (*main_func)(void); + WorkerThread *proxmarkThread; public: - ProxGuiQT(int argc, char **argv); + ProxGuiQT(int argc, char **argv, WorkerThread *wthread); ~ProxGuiQT(void); void ShowGraphWindow(void); void RepaintGraphWindow(void); @@ -112,6 +125,7 @@ class ProxGuiQT : public QObject void _RepaintGraphWindow(void); void _HideGraphWindow(void); void _Exit(void); + void _StartProxmarkThread(void); signals: void ShowGraphWindowSignal(void); void RepaintGraphWindowSignal(void); @@ -119,16 +133,4 @@ class ProxGuiQT : public QObject void ExitSignal(void); }; - -class WorkerThread : public QThread { - Q_OBJECT; -public: - WorkerThread(char*, bool); - ~WorkerThread(); - void run(); -private: - char *script_cmds_file = NULL; - bool usb_present; -}; - #endif // PROXGUI_QT diff --git a/client/proxmark3.c b/client/proxmark3.c index 956eb6a8..6fb066e8 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -9,6 +9,8 @@ // Main binary //----------------------------------------------------------------------------- +#include "proxmark3.h" + #include #include #include @@ -17,130 +19,97 @@ #include #include -#include "proxmark3.h" +#include "util_posix.h" #include "proxgui.h" #include "cmdmain.h" -#include "uart.h" #include "ui.h" +#include "util.h" #include "cmdparser.h" #include "cmdhw.h" #include "whereami.h" +#include "comms.h" - -// a global mutex to prevent interlaced printing from different threads -pthread_mutex_t print_lock; - -static serial_port sp; -static UsbCommand txcmd; -volatile static bool txcmd_pending = false; - -void SendCommand(UsbCommand *c) { - #if 0 - printf("Sending %d bytes\n", sizeof(UsbCommand)); - #endif - - if (offline) { - PrintAndLog("Sending bytes to proxmark failed - offline"); - return; - } - /** - The while-loop below causes hangups at times, when the pm3 unit is unresponsive - or disconnected. The main console thread is alive, but comm thread just spins here. - Not good.../holiman - **/ - while(txcmd_pending); - txcmd = *c; - txcmd_pending = true; -} - -struct receiver_arg { - int run; -}; - -byte_t rx[sizeof(UsbCommand)]; -byte_t* prx = rx; - -static void *uart_receiver(void *targ) { - struct receiver_arg *arg = (struct receiver_arg*)targ; - size_t rxlen; - - while (arg->run) { - rxlen = 0; - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen)) { - prx += rxlen; - if (prx-rx < sizeof(UsbCommand)) { - continue; - } - - UsbCommandReceived((UsbCommand*)rx); - } - prx = rx; - - if(txcmd_pending) { - if (!uart_send(sp, (byte_t*) &txcmd, sizeof(UsbCommand))) { - PrintAndLog("Sending bytes to proxmark failed"); - } - txcmd_pending = false; - } - } - - pthread_exit(NULL); - return NULL; -} - - -void main_loop(char *script_cmds_file, bool usb_present) { - struct receiver_arg rarg; +void +#ifdef __has_attribute +#if __has_attribute(force_align_arg_pointer) +__attribute__((force_align_arg_pointer)) +#endif +#endif +main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { char *cmd = NULL; - pthread_t reader_thread; + bool execCommand = (script_cmd != NULL); + bool stdinOnPipe = !isatty(STDIN_FILENO); if (usb_present) { - rarg.run = 1; - pthread_create(&reader_thread, NULL, &uart_receiver, &rarg); + SetOffline(false); // cache Version information now: CmdVersion(NULL); + } else { + SetOffline(true); } + // file with script FILE *script_file = NULL; - char script_cmd_buf[256]; // iceman, needs lua script the same file_path_buffer as the rest + char script_cmd_buf[256] = {0}; // iceman, needs lua script the same file_path_buffer as the rest if (script_cmds_file) { script_file = fopen(script_cmds_file, "r"); if (script_file) { - printf("using 'scripting' commands file %s\n", script_cmds_file); + printf("executing commands from file: %s\n", script_cmds_file); } } read_history(".history"); - while(1) { - + while (1) { // If there is a script file if (script_file) { + memset(script_cmd_buf, 0, sizeof(script_cmd_buf)); if (!fgets(script_cmd_buf, sizeof(script_cmd_buf), script_file)) { fclose(script_file); script_file = NULL; } else { - char *nl; - nl = strrchr(script_cmd_buf, '\r'); - if (nl) *nl = '\0'; - - nl = strrchr(script_cmd_buf, '\n'); - if (nl) *nl = '\0'; + strcleanrn(script_cmd_buf, sizeof(script_cmd_buf)); - if ((cmd = (char*) malloc(strlen(script_cmd_buf) + 1)) != NULL) { - memset(cmd, 0, strlen(script_cmd_buf)); - strcpy(cmd, script_cmd_buf); - printf("%s\n", cmd); + if ((cmd = strmcopy(script_cmd_buf)) != NULL) { + printf(PROXPROMPT"%s\n", cmd); + } + } + } else { + // If there is a script command + if (execCommand){ + if ((cmd = strmcopy(script_cmd)) != NULL) { + printf(PROXPROMPT"%s\n", cmd); + } + + execCommand = false; + } else { + // exit after exec command + if (script_cmd) + break; + + // if there is a pipe from stdin + if (stdinOnPipe) { + memset(script_cmd_buf, 0, sizeof(script_cmd_buf)); + if (!fgets(script_cmd_buf, sizeof(script_cmd_buf), stdin)) { + printf("\nStdin end. Exit...\n"); + break; + } + strcleanrn(script_cmd_buf, sizeof(script_cmd_buf)); + + if ((cmd = strmcopy(script_cmd_buf)) != NULL) { + printf(PROXPROMPT"%s\n", cmd); + } + + } else { + // read command from command prompt + cmd = readline(PROXPROMPT); } } } - if (!script_file) { - cmd = readline(PROXPROMPT); - } - + // execute command if (cmd) { while(cmd[strlen(cmd) - 1] == ' ') @@ -154,24 +123,19 @@ void main_loop(char *script_cmds_file, bool usb_present) { } } free(cmd); + cmd = NULL; } else { printf("\n"); break; } } - + write_history(".history"); - - if (usb_present) { - rarg.run = 0; - pthread_join(reader_thread, NULL); - } if (script_file) { fclose(script_file); script_file = NULL; } - } static void dumpAllHelp(int markdown) @@ -212,95 +176,141 @@ static void set_my_executable_path(void) } } +static void show_help(bool showFullHelp, char *command_line){ + printf("syntax: %s [-h|-help|-m|-f|-flush|-w|-wait|-c|-command|-l|-lua] [cmd_script_file_name] [command][lua_script_name]\n", command_line); + printf("\texample: %s "SERIAL_PORT_H"\n\n", command_line); + + if (showFullHelp){ + printf("help: <-h|-help> Dump all interactive command's help at once.\n"); + printf("\t%s -h\n\n", command_line); + printf("markdown: <-m> Dump all interactive help at once in markdown syntax\n"); + printf("\t%s -m\n\n", command_line); + printf("flush: <-f|-flush> Output will be flushed after every print.\n"); + printf("\t%s -f\n\n", command_line); + printf("wait: <-w|-wait> 20sec waiting the serial port to appear in the OS\n"); + printf("\t%s "SERIAL_PORT_H" -w\n\n", command_line); + printf("script: A script file with one proxmark3 command per line.\n\n"); + printf("command: <-c|-command> Execute one proxmark3 command.\n"); + printf("\t%s "SERIAL_PORT_H" -c \"hf mf chk 1* ?\"\n", command_line); + printf("\t%s "SERIAL_PORT_H" -command \"hf mf nested 1 *\"\n\n", command_line); + printf("lua: <-l|-lua> Execute lua script.\n"); + printf("\t%s "SERIAL_PORT_H" -l hf_read\n\n", command_line); + } +} int main(int argc, char* argv[]) { srand(time(0)); + bool usb_present = false; + bool waitCOMPort = false; + bool executeCommand = false; + bool addLuaExec = false; + char *script_cmds_file = NULL; + char *script_cmd = NULL; + if (argc < 2) { - printf("syntax: %s \n\n",argv[0]); - printf("\tLinux example:'%s /dev/ttyACM0'\n\n", argv[0]); - printf("help: %s -h\n\n", argv[0]); - printf("\tDump all interactive help at once\n"); - printf("markdown: %s -m\n\n", argv[0]); - printf("\tDump all interactive help at once in markdown syntax\n"); + show_help(true, argv[0]); return 1; } - if (strcmp(argv[1], "-h") == 0) { - printf("syntax: %s \n\n",argv[0]); - printf("\tLinux example:'%s /dev/ttyACM0'\n\n", argv[0]); - dumpAllHelp(0); - return 0; - } - if (strcmp(argv[1], "-m") == 0) { - dumpAllHelp(1); - return 0; - } - set_my_executable_path(); - - bool usb_present = false; - char *script_cmds_file = NULL; - - sp = uart_open(argv[1]); - if (sp == INVALID_SERIAL_PORT) { - printf("ERROR: invalid serial port\n"); - usb_present = false; - offline = 1; - } else if (sp == CLAIMED_SERIAL_PORT) { - printf("ERROR: serial port is claimed by another process\n"); - usb_present = false; - offline = 1; - } else { - usb_present = true; - offline = 0; - } - - // If the user passed the filename of the 'script' to execute, get it - if (argc > 2 && argv[2]) { - if (argv[2][0] == 'f' && //buzzy, if a word 'flush' passed, flush the output after every log entry. - argv[2][1] == 'l' && - argv[2][2] == 'u' && - argv[2][3] == 's' && - argv[2][4] == 'h') - { - printf("Output will be flushed after every print.\n"); - flushAfterWrite = 1; + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i],"-help") == 0) { + show_help(false, argv[0]); + dumpAllHelp(0); + return 0; + } + + if (strcmp(argv[i], "-m") == 0) { + dumpAllHelp(1); + return 0; + } + + if(strcmp(argv[i],"-f") == 0 || strcmp(argv[i],"-flush") == 0){ + printf("Output will be flushed after every print.\n"); + SetFlushAfterWrite(true); + } + + if(strcmp(argv[i],"-w") == 0 || strcmp(argv[i],"-wait") == 0){ + waitCOMPort = true; + } + + if(strcmp(argv[i],"-c") == 0 || strcmp(argv[i],"-command") == 0){ + executeCommand = true; + } + + if(strcmp(argv[i],"-l") == 0 || strcmp(argv[i],"-lua") == 0){ + executeCommand = true; + addLuaExec = true; } - else - script_cmds_file = argv[2]; } - // create a mutex to avoid interlacing print commands from our different threads - pthread_mutex_init(&print_lock, NULL); + // If the user passed the filename of the 'script' to execute, get it from last parameter + if (argc > 2 && argv[argc - 1] && argv[argc - 1][0] != '-') { + if (executeCommand){ + script_cmd = argv[argc - 1]; + + while(script_cmd[strlen(script_cmd) - 1] == ' ') + script_cmd[strlen(script_cmd) - 1] = 0x00; + + if (strlen(script_cmd) == 0) { + script_cmd = NULL; + } else { + if (addLuaExec){ + // add "script run " to command + char *ctmp = NULL; + int len = strlen(script_cmd) + 11 + 1; + if ((ctmp = (char*) malloc(len)) != NULL) { + memset(ctmp, 0, len); + strcpy(ctmp, "script run "); + strcpy(&ctmp[11], script_cmd); + script_cmd = ctmp; + } + } + + printf("Execute command from commandline: %s\n", script_cmd); + } + } else { + script_cmds_file = argv[argc - 1]; + } + } + + // check command + if (executeCommand && (!script_cmd || strlen(script_cmd) == 0)){ + printf("ERROR: execute command: command not found.\n"); + return 2; + } + + // set global variables + set_my_executable_path(); + + // try to open USB connection to Proxmark + usb_present = OpenProxmark(argv[1], waitCOMPort, 20, false); #ifdef HAVE_GUI #ifdef _WIN32 - InitGraphics(argc, argv, script_cmds_file, usb_present); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); MainGraphics(); #else char* display = getenv("DISPLAY"); if (display && strlen(display) > 1) { - InitGraphics(argc, argv, script_cmds_file, usb_present); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); MainGraphics(); } else { - main_loop(script_cmds_file, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present); } #endif #else - main_loop(script_cmds_file, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present); #endif // Clean up the port if (usb_present) { - uart_close(sp); + CloseProxmark(); } - // clean up mutex - pthread_mutex_destroy(&print_lock); - exit(0); } diff --git a/client/proxmark3.h b/client/proxmark3.h index 7ff7f676..86d09bc0 100644 --- a/client/proxmark3.h +++ b/client/proxmark3.h @@ -20,10 +20,9 @@ extern "C" { #endif -void SendCommand(UsbCommand *c); const char *get_my_executable_path(void); const char *get_my_executable_directory(void); -void main_loop(char *script_cmds_file, bool usb_present); +void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present); #ifdef __cplusplus } diff --git a/client/reveng/bmpbit.c b/client/reveng/bmpbit.c deleted file mode 100644 index 39a29e61..00000000 --- a/client/reveng/bmpbit.c +++ /dev/null @@ -1,86 +0,0 @@ -/* bmpbit.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng 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. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -#ifdef BMPTST -# include -# include -#else -# define FILE void -#endif -#include "reveng.h" - -#if (defined BMPTST) || (BMP_BIT < 32) -/* Size in bits of a bmp_t. Not necessarily a power of two. */ -int bmpbit; - -/* The highest power of two that is strictly less than BMP_BIT. - * Initialises the index of a binary search for set bits in a bmp_t. - * (Computed correctly for BMP_BIT >= 2) - */ -int bmpsub; - -void -setbmp(void) { - /* Initialise BMP_BIT and BMP_SUB for the local architecture. */ - bmp_t bmpmax = ~(bmp_t) 0; - - bmpbit = 0; bmpsub = 1; - - while(bmpmax) { - bmpmax <<= 1; - ++bmpbit; - } - - while((bmpsub | (bmpsub - 1)) < bmpbit - 1) - bmpsub <<= 1; -} -#endif - -#ifdef BMPTST -int -main(int argc, char *argv[]) { - /* check the compile-time bitmap width is correct, otherwise - * searches run forever. */ -# if BMP_BIT > 0 - setbmp(); - if(BMP_BIT != bmpbit || BMP_SUB != bmpsub) { - fprintf(stderr,"reveng: configuration fault. Update " - "config.h with these definitions and " - "recompile:\n" - "\t#define BMP_BIT %d\n" - "\t#define BMP_SUB %d\n", - bmpbit, bmpsub); - exit(EXIT_FAILURE); - } -# endif /* BMP_BIT > 0 */ - /* check the bitmap constant macro */ - if(~(bmp_t) 0 != ~BMP_C(0)) { - fprintf(stderr, "reveng: configuration fault. Edit " - "the definition of BMP_C() in config.h to " - "match BMP_T and recompile.\n"); - exit(EXIT_FAILURE); - } - exit(EXIT_SUCCESS); -} - -#endif /* BMPTST */ diff --git a/client/reveng/cli.c b/client/reveng/cli.c deleted file mode 100644 index 224eedd6..00000000 --- a/client/reveng/cli.c +++ /dev/null @@ -1,628 +0,0 @@ -/* cli.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng 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. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -/* 2015-04-03: added -z - * 2013-09-16: do not search with -M - * 2013-06-11: uprog() suppresses first progress report - * 2013-04-22: uprog() prints poly same as mtostr() - * 2013-02-07: added -q, uprog(), removed -W, R_ODDLY - * 2012-05-24: -D dumps parameters of all models - * 2012-03-03: added code to test sort order of model table - * 2012-02-20: set stdin to binary (MinGW). offer -D if preset unknown. - * 2011-09-06: -s reads arguments once. stdin not closed. - * 2011-09-06: fixed bad argument-freeing loops. - * 2011-08-27: validates BMP_C() - * 2011-08-26: validates BMPBIT and BMPSUB - * 2011-08-25: fixed Init/Xorout reflection logic in -V and -v - * 2011-01-17: fixed ANSI C warnings - * 2011-01-15: added NOFORCE - * 2011-01-14: added -k, -P - * 2011-01-10: reorganised switches, added -V, -X - * 2010-12-26: renamed CRC RevEng - * 2010-12-18: implemented -c, -C - * 2010-12-14: added and implemented -d, -D, fixed -ipx entry - * 2010-12-11: implemented -e. first tests - * 2010-12-10: finalised option processing. started input validation - * 2010-12-07: started cli - */ - -#include -#include -#include "getopt.h" -#ifdef _WIN32 -# include -# include -# ifndef STDIN_FILENO -# define STDIN_FILENO 0 -# endif /* STDIN_FILENO */ -#endif /* _WIN32 */ - -#include "reveng.h" - -static FILE *oread(const char *); -static poly_t rdpoly(const char *, int, int); -static void usage(void); - -static const char *myname = "reveng"; /* name of our program */ - -int reveng_main(int argc, char *argv[]) { - /* Command-line interface for CRC RevEng. - * Process options and switches in the argument list and - * run the required function. - */ - - /* default values */ - model_t model = { - PZERO, /* no CRC polynomial, user must specify */ - PZERO, /* Init = 0 */ - P_BE, /* RefIn = false, RefOut = false, plus P_RTJUST setting in reveng.h */ - PZERO, /* XorOut = 0 */ - PZERO, /* check value unused */ - NULL /* no model name */ - }; - int ibperhx = 8, obperhx = 8; - int rflags = 0, uflags = 0; /* search and UI flags */ - - unsigned long width = 0UL; - int c, mode = 0, args, psets, pass; - poly_t apoly, crc, qpoly = PZERO, *apolys, *pptr = NULL, *qptr = NULL; - model_t pset = model, *candmods, *mptr; - char *string; - - myname = argv[0]; - - /* stdin must be binary */ -#ifdef _WIN32 - _setmode(STDIN_FILENO, _O_BINARY); -#endif /* _WIN32 */ - - SETBMP(); - - pos=0; - optind=1; - do { - c=getopt(argc, argv, "?A:BDFLMP:SVXa:bcdefhi:k:lm:p:q:rstuvw:x:yz"); - switch(c) { - case 'A': /* A: bits per output character */ - case 'a': /* a: bits per character */ - if((obperhx = atoi(optarg)) > BMP_BIT) { - fprintf(stderr,"%s: argument to -%c must be between 1 and %d\n", myname, c, BMP_BIT); - return 0; - //exit(EXIT_FAILURE); - } - if(c == 'a') ibperhx = obperhx; - break; - case 'b': /* b big-endian (RefIn = false, RefOut = false ) */ - model.flags &= ~P_REFIN; - rflags |= R_HAVERI; - /* fall through: */ - case 'B': /* B big-endian output (RefOut = false) */ - model.flags &= ~P_REFOUT; - rflags |= R_HAVERO; - mnovel(&model); - /* fall through: */ - case 'r': /* r right-justified */ - model.flags |= P_RTJUST; - break; - case 'c': /* c calculate CRC */ - case 'D': /* D list primary model names */ - case 'd': /* d dump CRC model */ - case 'e': /* e echo arguments */ - case 's': /* s search for algorithm */ - case 'v': /* v calculate reversed CRC */ - if(mode) { - fprintf(stderr,"%s: more than one mode switch specified. Use %s -h for help.\n", myname, myname); - return 0; - //exit(EXIT_FAILURE); - } - mode = c; - break; - case 'F': /* F force search */ -#ifndef NOFORCE - uflags |= C_FORCE; -#endif - break; - case 'f': /* f arguments are filenames */ - uflags |= C_INFILE; - break; - case 'h': /* h get help / usage */ - case 'u': /* u get help / usage */ - case '?': /* ? get help / usage */ - default: - usage(); - return 0; - //exit(EXIT_FAILURE); - break; - case 'i': /* i: Init value */ - pptr = &model.init; - rflags |= R_HAVEI; - goto ippx; - case 'k': /* k: polynomial in Koopman notation */ - pfree(&model.spoly); - model.spoly = strtop(optarg, 0, 4); - pkchop(&model.spoly); - width = plen(model.spoly); - rflags |= R_HAVEP; - mnovel(&model); - break; - case 'l': /* l little-endian input and output */ - model.flags |= P_REFIN; - rflags |= R_HAVERI; - /* fall through: */ - case 'L': /* L little-endian output */ - model.flags |= P_REFOUT; - rflags |= R_HAVERO; - mnovel(&model); - /* fall through: */ - case 't': /* t left-justified */ - model.flags &= ~P_RTJUST; - break; - case 'm': /* m: select preset CRC model */ - if(!(c = mbynam(&model, optarg))) { - fprintf(stderr,"%s: preset model '%s' not found. Use %s -D to list presets.\n", myname, optarg, myname); - return 0; - //exit(EXIT_FAILURE); - } - if(c < 0){ - uerror("no preset models available"); - return 0; - } - /* must set width so that parameter to -ipx is not zeroed */ - width = plen(model.spoly); - rflags |= R_HAVEP | R_HAVEI | R_HAVERI | R_HAVERO | R_HAVEX; - break; - case 'M': /* M non-augmenting algorithm */ - model.flags &= ~P_MULXN; - break; - case 'P': /* P: reversed polynomial */ - case 'p': /* p: polynomial */ - pptr = &model.spoly; - rflags &= ~R_HAVEQ; - rflags |= R_HAVEP; -ippx: - pfree(pptr); - *pptr = strtop(optarg, 0, 4); - pright(pptr, width); - if(c == 'P') - prev(pptr); - mnovel(&model); - break; - case 'q': /* q: range end polynomial */ - pptr = &qpoly; - rflags &= ~R_HAVEP; - rflags |= R_HAVEQ; - goto ippx; - case 'S': /* s space between output characters */ - model.flags |= P_SPACE; - break; - case 'V': /* v reverse algorithm */ - /* Distinct from the -v switch as the - * user will have to reverse his or her - * own arguments. The user cannot dump - * the model generated by -v either. - */ - mrev(&model); - break; - case 'w': /* w: CRC width = order - 1 */ - width = (unsigned long) atol(optarg); - break; - case 'X': /* X print uppercase hex */ - model.flags |= P_UPPER; - break; - case 'x': /* x: XorOut value */ - pptr = &model.xorout; - rflags |= R_HAVEX; - goto ippx; - case 'y': /* y little-endian byte order in files */ - model.flags |= P_LTLBYT; - break; - case 'z': /* z raw binary arguments */ - model.flags |= P_DIRECT; - break; - case -1: /* no more options, continue */ - ; - } - } while(c != -1); - - /* canonicalise the model, so the one we dump is the one we - * calculate with (not with -s, spoly may be blank which will - * normalise to zero and clear init and xorout.) - */ - if(mode != 's') - mcanon(&model); - - switch(mode) { - case 'v': /* v calculate reversed CRC */ - /* Distinct from the -V switch as this causes - * the arguments and output to be reversed as well. - */ - /* reciprocate Poly */ - prcp(&model.spoly); - - /* mrev() does: - * if(refout) prev(init); else prev(xorout); - * but here the entire argument polynomial is - * reflected, not just the characters, so RefIn - * and RefOut are not inverted as with -V. - * Consequently Init is the mirror image of the - * one resulting from -V, and so we have: - */ - if(~model.flags & P_REFOUT) { - prev(&model.init); - prev(&model.xorout); - } - - /* swap init and xorout */ - apoly = model.init; - model.init = model.xorout; - model.xorout = apoly; - - /* fall through: */ - case 'c': /* c calculate CRC */ - - /* validate inputs */ - /* if(plen(model.spoly) == 0) { - * fprintf(stderr,"%s: no polynomial specified for -%c (add -w WIDTH -p POLY)\n", myname, mode); - * exit(EXIT_FAILURE); - * } - */ - - /* in the Williams model, xorout is applied after the refout stage. - * as refout is part of ptostr(), we reverse xorout here. - */ - if(model.flags & P_REFOUT) - prev(&model.xorout); - - for(; optind < argc; ++optind) { - if(uflags & C_INFILE) - apoly = rdpoly(argv[optind], model.flags, ibperhx); - else - apoly = strtop(argv[optind], model.flags, ibperhx); - - if(mode == 'v') - prev(&apoly); - - crc = pcrc(apoly, model.spoly, model.init, model.xorout, model.flags); - - if(mode == 'v') - prev(&crc); - - string = ptostr(crc, model.flags, obperhx); - puts(string); - free(string); - pfree(&crc); - pfree(&apoly); - } - break; - case 'D': /* D dump all models */ - args = mcount(); - if(!args){ - uerror("no preset models available"); - return 0; - } - for(mode = 0; mode < args; ++mode) { - mbynum(&model, mode); - mcanon(&model); - ufound(&model); - } - break; - case 'd': /* d dump CRC model */ - /* maybe we don't want to do this: - * either attaching names to arbitrary models or forcing to a preset - * mmatch(&model, M_OVERWR); - */ - if(~model.flags & P_MULXN){ - uerror("not a Williams model compliant algorithm"); - return 0; - } - string = mtostr(&model); - puts(string); - free(string); - break; - case 'e': /* e echo arguments */ - for(; optind < argc; ++optind) { - if(uflags & C_INFILE) - apoly = rdpoly(argv[optind], model.flags, ibperhx); - else - apoly = strtop(argv[optind], model.flags, ibperhx); - - psum(&apoly, model.init, 0UL); - string = ptostr(apoly, model.flags, obperhx); - puts(string); - free(string); - pfree(&apoly); - } - break; - case 's': /* s search for algorithm */ - if(!width){ - uerror("must specify positive -k or -w before -s"); - return 0; - } - if(~model.flags & P_MULXN){ - uerror("cannot search for non-Williams compliant models"); - return 0; - } - praloc(&model.spoly, width); - praloc(&model.init, width); - praloc(&model.xorout, width); - if(!plen(model.spoly)) - palloc(&model.spoly, width); - else - width = plen(model.spoly); - - /* special case if qpoly is zero, search to end of range */ - if(!ptst(qpoly)) - rflags &= ~R_HAVEQ; - - /* allocate argument array */ - args = argc - optind; - if(!(apolys = malloc(args * sizeof(poly_t)))){ - uerror("cannot allocate memory for argument list"); - return 0; - } - - for(pptr = apolys; optind < argc; ++optind) { - if(uflags & C_INFILE) - *pptr++ = rdpoly(argv[optind], model.flags, ibperhx); - else - *pptr++ = strtop(argv[optind], model.flags, ibperhx); - } - /* exit value of pptr is used hereafter! */ - - /* if endianness not specified, try - * little-endian then big-endian. - * NB: crossed-endian algorithms will not be - * searched. - */ - - /* scan against preset models */ - if(~uflags & C_FORCE) { - pass = 0; - do { - psets = mcount(); - while(psets) { - mbynum(&pset, --psets); - /* skip if different width, or refin or refout don't match */ - if(plen(pset.spoly) != width || (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)) - continue; - if(rflags & R_HAVEI && psncmp(&model.init, &pset.init)) - continue; - if(rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) - continue; - apoly = pclone(pset.xorout); - if(pset.flags & P_REFOUT) - prev(&apoly); - for(qptr = apolys; qptr < pptr; ++qptr) { - crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0); - if(ptst(crc)) { - pfree(&crc); - break; - } else - pfree(&crc); - } - pfree(&apoly); - if(qptr == pptr) { - /* the selected model solved all arguments */ - mcanon(&pset); - ufound(&pset); - uflags |= C_RESULT; - } - } - mfree(&pset); - - /* toggle refIn/refOut and reflect arguments */ - 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); - } - if(uflags & C_RESULT) { - for(qptr = apolys; qptr < pptr; ++qptr) - pfree(qptr); - //return 1; - //exit(EXIT_SUCCESS); - } - if(!(model.flags & P_REFIN) != !(model.flags & P_REFOUT)){ - uerror("cannot search for crossed-endian models"); - return 0; - } - pass = 0; - do { - mptr = candmods = reveng(&model, qpoly, rflags, args, apolys); - if(mptr && plen(mptr->spoly)) - uflags |= C_RESULT; - while(mptr && plen(mptr->spoly)) { - /* results were printed by the callback - * string = mtostr(mptr); - * puts(string); - * free(string); - */ - 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) - pfree(qptr); - free(apolys); - if(~uflags & C_RESULT) - uerror("no models found"); - break; - default: /* no mode specified */ - fprintf(stderr, "%s: no mode switch specified. Use %s -h for help.\n", myname, myname); - //exit(EXIT_FAILURE); - } - - return 1; - //exit(EXIT_SUCCESS); -} - -void -ufound(const model_t *model) { - /* Callback function to report each model found */ - char *string; - - if(!model) return; - /* generated models will be canonical */ - string = mtostr(model); - puts(string); - free(string); -} - -void -uerror(const char *msg) { - /* Callback function to report fatal errors */ - fprintf(stderr, "%s: %s\n", myname, msg); - return; - //exit(EXIT_FAILURE); -} - -void -uprog(const poly_t gpoly, int flags, unsigned long seq) { - /* Callback function to report search progress */ - char *string; - - /* Suppress first report in CLI */ - if(!seq) - return; - string = ptostr(gpoly, P_RTJUST, 4); - fprintf(stderr, "%s: searching: width=%ld poly=0x%s refin=%s refout=%s\n", - myname, plen(gpoly), string, - (flags & P_REFIN ? "true" : "false"), - (flags & P_REFOUT ? "true" : "false") - ); - free(string); -} - -static poly_t -rdpoly(const char *name, int flags, int bperhx) { - /* read poly from file in chunks and report errors */ - - poly_t apoly = PZERO, chunk = PZERO; - FILE *input; - - input = oread(name); - while(!feof(input) && !ferror(input)) { - chunk = filtop(input, BUFFER, flags, bperhx); - psum(&apoly, chunk, plen(apoly)); - pfree(&chunk); - } - if(ferror(input)) { - fprintf(stderr,"%s: error condition on file '%s'\n", myname, name); - exit(EXIT_FAILURE); - } - /* close file unless stdin */ - if(input == stdin) - /* reset EOF condition */ - clearerr(input); - else if(fclose(input)) { - fprintf(stderr,"%s: error closing file '%s'\n", myname, name); - exit(EXIT_FAILURE); - } - return(apoly); -} - -static FILE * -oread(const char *name) { - /* open file for reading and report errors */ - FILE *handle; - - /* recognise special name '-' as standard input */ - if(*name == '-' && name[1] == '\0') - return(stdin); - if(!(handle = fopen(name, "rb"))) { - fprintf(stderr, "%s: cannot open '%s' for reading\n", myname, name); - return 0; - //exit(EXIT_FAILURE); - } - return(handle); -} - -static void -usage(void) { - /* print usage if asked, or if syntax incorrect */ - fprintf(stderr, - "CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder\n" - "Usage:\t"); - fputs(myname, stderr); - fprintf(stderr, - "\t-cdDesvhu? [-bBfFlLMrStVXyz]\n" - "\t\t[-a BITS] [-A OBITS] [-i INIT] [-k KPOLY] [-m MODEL]\n" - "\t\t[-p POLY] [-P RPOLY] [-q QPOLY] [-w WIDTH] [-x XOROUT]\n" - "\t\t[STRING...]\n" - "Options:\n" - "\t-a BITS\t\tbits per character (1 to %d)\n" - "\t-A OBITS\tbits per output character (1 to %d)\n" - "\t-i INIT\t\tinitial register value\n" - "\t-k KPOLY\tgenerator in Koopman notation (implies WIDTH)\n" - "\t-m MODEL\tpreset CRC algorithm\n" - "\t-p POLY\t\tgenerator or search range start polynomial\n" - "\t-P RPOLY\treversed generator polynomial\n", - BMP_BIT, BMP_BIT); - fprintf(stderr, - "\t-q QPOLY\tsearch range end polynomial\n" - "\t-w WIDTH\tregister size, in bits\n" - "\t-x XOROUT\tfinal register XOR value\n" - "Modifier switches:\n" - "\t-b big-endian CRC\t\t-B big-endian CRC output\n" - "\t-f read files named in STRINGs\t-F find presets less quickly\n" - "\t-l little-endian CRC\t\t-L little-endian CRC output\n" - "\t-M non-augmenting algorithm\t-r right-justified output\n" - "\t-S print spaces between chars\t-t left-justified output\n" - "\t-V reverse algorithm only\t-X print uppercase hex\n" - "\t-y low bytes first in files\t-z raw binary STRINGs\n"); - fprintf(stderr, - "Mode switches:\n" - "\t-c calculate CRCs\t\t-d dump algorithm parameters\n" - "\t-D list preset algorithms\t-e echo (and reformat) input\n" - "\t-s search for algorithm\t\t-v calculate reversed CRCs\n" - "\t-g search for alg given hex+crc\t-h | -u | -? show this help\n" - "Common Use Examples:\n" - "\t reveng -g 01020304e3\n" - "\t Searches for a known/common crc preset that computes the crc\n" - "\t on the end of the given hex string\n" - "\t reveng -w 8 -s 01020304e3 010204039d\n" - "\t Searches for any possible 8 bit width crc calc that computes\n" - "\t the crc on the end of the given hex string(s)\n" - "\t reveng -m CRC-8 -c 01020304\n" - "\t Calculates the crc-8 of the given hex string\n" - "\t reveng -D\n" - "\t Outputs a list of all known/common crc models with their\n" - "\t preset values\n" - "\n" - "Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook\n" - "This is free software; see the source for copying conditions. There is NO\n" - "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" - "Version " - VERSION - "\t\t\t\t \n"); -} diff --git a/client/reveng/config.h b/client/reveng/config.h deleted file mode 100644 index 2f5108f0..00000000 --- a/client/reveng/config.h +++ /dev/null @@ -1,96 +0,0 @@ -/* config.h - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng 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. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -#ifndef CONFIG_H -#define CONFIG_H 1 - -/***************************************** - * * - * Start of user configuration options * - * * - *****************************************/ - -/* A type to contain polynomial coefficient bitmaps. - * Can be changed to 'unsigned long long' for some extended compilers. - * Adjust BMP_C(), BMP_BIT and BMP_SUB below if this is changed. - */ - -#define BMP_T unsigned long - -/* Creates an appropriate numeric constant for bmp_t. - * If the underlying type is 'unsigned long long', change UL to ULL. - */ - -#define BMP_C(n) (n##UL) - -/* Define BMPMACRO to turn the definitions of the size of a bmp_t into - * compile-time constants. This improves efficiency but makes the code - * platform-specific. - */ - -/* #define BMPMACRO 1 */ - -/* Some enterprise users may wish to disable the -F switch to minimise CPU - * usage. To do this, define the macro NOFORCE. - */ - -/* #define NOFORCE 1 */ - -/* Define PRESETS to compile CRC RevEng with the preset models from the - * CRC Catalogue. This implies BMPMACRO and so makes the code platform- - * specific. - */ - - -// PM3 NOTES: -// only PRESETS tested on windows -// PRESETS was commented out of original code -// but to enable preset models it must be enabled -// (marshmellow) -#define PRESETS 1 - - -/* Macros defining the size of a bmp_t. - * Their values only matter if PRESETS and/or BMPMACRO are defined, in - * which case edit the macros below to suit your architecture. - * Otherwise, BMP_BIT and BMP_SUB will be redefined as aliases of bmpbit - * and bmpsub, global objects initialised at run time. - */ - -/* Size in bits of a bmp_t. Not necessarily a power of two. */ - -#define BMP_BIT 32 - -/* The highest power of two that is strictly less than BMP_BIT. - * Initialises the index of a binary search for set bits in a bmp_t. - */ - -#define BMP_SUB 16 - -/***************************************** - * * - * End of user configuration options * - * * - *****************************************/ - -#endif /* CONFIG_H */ diff --git a/client/reveng/getopt.c b/client/reveng/getopt.c deleted file mode 100644 index abe99634..00000000 --- a/client/reveng/getopt.c +++ /dev/null @@ -1,81 +0,0 @@ -/*---------------------------------------------------------------------- - - Replacement for Unix "getopt()", for DOS/Windows/etc. - - getopt.c 1.3 2003/09/17 16:17:59 - - Copyright (C) 1998, 2003 by David A. Hinds -- All Rights Reserved - - This file is part of ASPEX. - - ASPEX is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - ASPEX is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with ASPEX; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -----------------------------------------------------------------------*/ - -#include "string.h" -#include "stdio.h" -#include "getopt.h" - -char *optarg; -int optind = 1, opterr, optopt; -int pos = 0; -int getopt(int argc, char *argv[], const char *optstring) -{ - //static int pos = 0; - char *str; - - if (pos == 0) { - if ((optind >= argc) || (*argv[optind] != '-')) - return EOF; - pos = 1; - if (argv[optind][pos] == '\0') - return EOF; - } - - str = strchr(optstring, argv[optind][pos]); - if (str == NULL) { - optopt = argv[optind][pos]; - if (opterr) - fprintf(stderr, "%s: illegal option -- %c\n", argv[0], - optopt); - return '?'; - } - - if (str[1] == ':') { - if (argv[optind][pos+1] != '\0') { - optarg = &argv[optind][pos+1]; - return *str; - } - optind++; - if (optind >= argc) { - optopt = *str; - if (opterr) - fprintf(stderr, "%s: option requires an argument -- %c\n", - argv[0], optopt); - return '?'; - } - optarg = argv[optind]; - optind++; pos = 0; - return *str; - } - else { - pos++; - if (argv[optind][pos] == '\0') { - optind++; - pos = 0; - } - return *str; - } -} diff --git a/client/reveng/getopt.h b/client/reveng/getopt.h deleted file mode 100644 index bf66d9b1..00000000 --- a/client/reveng/getopt.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - getopt.h 1.2 2003/09/17 16:17:59 - - Copyright (C) 1998, 2003 by David A. Hinds -- All Rights Reserved - - This file is part of ASPEX. - - ASPEX is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - ASPEX is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with ASPEX; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -extern char *optarg; -extern int optind, opterr, optopt, pos; -int getopt(int argc, char *argv[], const char *optstring); diff --git a/client/reveng/model.c b/client/reveng/model.c deleted file mode 100644 index 2d45b2fe..00000000 --- a/client/reveng/model.c +++ /dev/null @@ -1,823 +0,0 @@ -/* model.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng 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. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -/* 2014-01-14: added CRC-8/DVB-S2 - * 2014-01-11: corrected CRC-40/GSM, added alias CRC-8/AES - * 2013-10-14: added CRC-13/BBC and six cdma2000 algorithms - * 2013-06-11: ensure BMP_BIT is an integer constant to compile presets - * 2013-01-20: big polynomials autogenerated, corrected CRC-82/DARC - * 2012-07-19: added CRC-8/EBU - * 2012-07-16: added CRC-15/MPT1327 - * 2012-05-25: removed CRC-1/PARITY-EVEN, CRC-1/PARITY-ODD - * 2012-04-12: added model CRC-31/PHILIPS - * 2012-03-03: single-line Williams model string conversion - * 2012-02-20: corrected model CRC-6/DARC - * 2011-09-03: added mrev(), mnovel() - * 2011-08-28: added model CRC-64/XZ - * 2011-04-30: added models CRC-16/TMS37157 and CRC-A, and alias CRC-B - * 2011-02-10: made preset models ANSI C compliant - * 2011-01-17: fixed ANSI C warnings (except preset models) - * 2011-01-01: added mbynum(), mcount() - * 2010-12-26: renamed CRC RevEng - * 2010-12-18: minor change to mtostr() output format - * 2010-12-15: added mcmp(), mmatch() - * 2010-12-14: finished mbynam(), mnames(), mtostr() - * 2010-12-13: restarted with PCONST macros - * 2010-12-12: was having so much fun I didn't think to try compiling. :( - * 2010-12-12: started models.c - */ - -#include -#include -#include -#include -#include "reveng.h" - -/* Private declarations */ - -struct mpreset { - const unsigned long width; /* width of CRC algorithm */ - const bmp_t *const bspoly; /* polynomial with highest-order term removed. length determines CRC width */ - const bmp_t *const binit; /* initial register value. length == poly.length */ - const int flags; /* P_REFIN and P_REFOUT indicate reflected input/output */ - const bmp_t *const bxorout; /* final register XOR mask. length == poly.length */ - const bmp_t *const bcheck; /* optional check value, the CRC of the UTF-8 string "123456789" */ - const char *const name; /* optional canonical name of the model */ -}; - -struct malias { - const char *name; - const struct mpreset *model; - const int isprimry; -}; - -#ifdef PRESETS -# if BMP_BIT < 32 -# error config.h: BMP_BIT must be an integer constant macro to compile presets -# else /* BMP_BIT */ - -/* Big polynomial constants. */ - -/* Directives for relink.pl */ -/* CONSTANT b40 = (40, 0x0004820009) */ -/* CONSTANT b40a = (40, 0xffffffffff) */ -/* CONSTANT b40b = (40, 0xd4164fc646) */ -/* CONSTANT b64 = (64, 0x42f0e1eba9ea3693) */ -/* CONSTANT b64a = (64, 0x6c40df5f0b497347) */ -/* CONSTANT b64b = (64, 0xffffffffffffffff) */ -/* CONSTANT b64c = (64, 0x62ec59e3f1a4f00a) */ -/* CONSTANT b64d = (64, 0x995dc9bbdf1939fa) */ -/* CONSTANT b82 = (82, 0x0308c0111011401440411) */ -/* CONSTANT b82a = (82, 0x09ea83f625023801fd612) */ - -/* The next section was generated by relink.pl from the directives above. */ - -/* DO NOT EDIT the section below, INCLUDING the next comment. */ -/* BEGIN AUTO-GENERATED CONSTANTS */ -# if BMP_BIT >= 40 -static const bmp_t b40[] = { - BMP_C(0x0004820009) << (BMP_BIT - 40), -}; -static const bmp_t b40a[] = { - BMP_C(0xffffffffff) << (BMP_BIT - 40), -}; -static const bmp_t b40b[] = { - BMP_C(0xd4164fc646) << (BMP_BIT - 40), -}; -# else /* BMP_BIT */ -static const bmp_t b40[] = { - BMP_C(0x00048200) << (BMP_BIT - 32) | BMP_C(0x04) >> (39 - BMP_BIT), - BMP_C(0x09) << (BMP_BIT * 2 - 40), -}; -static const bmp_t b40a[] = { - BMP_C(0xffffffff) << (BMP_BIT - 32) | BMP_C(0x7f) >> (39 - BMP_BIT), - BMP_C(0xff) << (BMP_BIT * 2 - 40), -}; -static const bmp_t b40b[] = { - BMP_C(0xd4164fc6) << (BMP_BIT - 32) | BMP_C(0x23) >> (39 - BMP_BIT), - BMP_C(0x46) << (BMP_BIT * 2 - 40), -}; -# endif /* BMP_BIT */ - -# if BMP_BIT >= 64 -static const bmp_t b64[] = { - BMP_C(0x42f0e1eba9ea3693) << (BMP_BIT - 64), -}; -static const bmp_t b64a[] = { - BMP_C(0x6c40df5f0b497347) << (BMP_BIT - 64), -}; -static const bmp_t b64b[] = { - BMP_C(0xffffffffffffffff) << (BMP_BIT - 64), -}; -static const bmp_t b64c[] = { - BMP_C(0x62ec59e3f1a4f00a) << (BMP_BIT - 64), -}; -static const bmp_t b64d[] = { - BMP_C(0x995dc9bbdf1939fa) << (BMP_BIT - 64), -}; -# else /* BMP_BIT */ -static const bmp_t b64[] = { - BMP_C(0x42f0e1eb) << (BMP_BIT - 32) | BMP_C(0x54f51b49) >> (63 - BMP_BIT), - BMP_C(0xa9ea3693) << (BMP_BIT * 2 - 64), -}; -static const bmp_t b64a[] = { - BMP_C(0x6c40df5f) << (BMP_BIT - 32) | BMP_C(0x05a4b9a3) >> (63 - BMP_BIT), - BMP_C(0x0b497347) << (BMP_BIT * 2 - 64), -}; -static const bmp_t b64b[] = { - BMP_C(0xffffffff) << (BMP_BIT - 32) | BMP_C(0x7fffffff) >> (63 - BMP_BIT), - BMP_C(0xffffffff) << (BMP_BIT * 2 - 64), -}; -static const bmp_t b64c[] = { - BMP_C(0x62ec59e3) << (BMP_BIT - 32) | BMP_C(0x78d27805) >> (63 - BMP_BIT), - BMP_C(0xf1a4f00a) << (BMP_BIT * 2 - 64), -}; -static const bmp_t b64d[] = { - BMP_C(0x995dc9bb) << (BMP_BIT - 32) | BMP_C(0x6f8c9cfd) >> (63 - BMP_BIT), - BMP_C(0xdf1939fa) << (BMP_BIT * 2 - 64), -}; -# endif /* BMP_BIT */ - -# if BMP_BIT >= 82 -static const bmp_t b82[] = { - BMP_C(0x0308c0111011401440411) << (BMP_BIT - 82), -}; -static const bmp_t b82a[] = { - BMP_C(0x09ea83f625023801fd612) << (BMP_BIT - 82), -}; -# elif BMP_BIT >= 41 -static const bmp_t b82[] = { - BMP_C(0x01846008880) << (BMP_BIT - 41) | BMP_C(0x08a00a20208) >> (81 - BMP_BIT), - BMP_C(0x11401440411) << (BMP_BIT * 2 - 82), -}; -static const bmp_t b82a[] = { - BMP_C(0x04f541fb128) << (BMP_BIT - 41) | BMP_C(0x011c00feb09) >> (81 - BMP_BIT), - BMP_C(0x023801fd612) << (BMP_BIT * 2 - 82), -}; -# else /* BMP_BIT */ -static const bmp_t b82[] = { - BMP_C(0x0c230044) << (BMP_BIT - 32) | BMP_C(0x040) >> (40 - BMP_BIT), - BMP_C(0x40450051) << (BMP_BIT * 2 - 64) | BMP_C(0x00104) >> (80 - BMP_BIT * 2), - BMP_C(0x00411) << (BMP_BIT * 3 - 82), -}; -static const bmp_t b82a[] = { - BMP_C(0x27aa0fd8) << (BMP_BIT - 32) | BMP_C(0x094) >> (40 - BMP_BIT), - BMP_C(0x9408e007) << (BMP_BIT * 2 - 64) | BMP_C(0x0f584) >> (80 - BMP_BIT * 2), - BMP_C(0x3d612) << (BMP_BIT * 3 - 82), -}; -# endif /* BMP_BIT */ - -/* END AUTO-GENERATED CONSTANTS */ -/* DO NOT EDIT the section above, INCLUDING the previous comment. */ - -/* Array of the polynomial bitmaps used in the model table. */ -static const bmp_t b32[] = { - BMP_C(0x00000000) << (BMP_BIT - 32), /* 0 -- 5, 00 */ - BMP_C(0x000000af) << (BMP_BIT - 32), /* 1 -- 32,000000af */ - BMP_C(0x00010000) << (BMP_BIT - 32), /* 2 -- 16, 0001 */ - BMP_C(0x00020000) << (BMP_BIT - 32), /* 3 -- 15, 0001 */ - BMP_C(0x007e0000) << (BMP_BIT - 32), /* 4 -- 16, 007e */ - BMP_C(0x007f0000) << (BMP_BIT - 32), /* 5 -- 16, 007f */ - BMP_C(0x03400000) << (BMP_BIT - 32), /* 6 -- 11, 01a */ - BMP_C(0x0376e6e7) << (BMP_BIT - 32), /* 7 -- 32,0376e6e7 */ - BMP_C(0x04c11db7) << (BMP_BIT - 32), /* 8 -- 32,04c11db7 */ - BMP_C(0x05890000) << (BMP_BIT - 32), /* 9 -- 16, 0589 */ - BMP_C(0x07000000) << (BMP_BIT - 32), /* 10 -- 8, 07 */ - BMP_C(0x09823b6e) << (BMP_BIT - 32), /* 11 -- 31,04c11db7 */ - BMP_C(0x0b3c0000) << (BMP_BIT - 32), /* 12 -- 15, 059e */ - BMP_C(0x0c000000) << (BMP_BIT - 32), /* 13 -- 6, 03 */ - BMP_C(0x0fb30000) << (BMP_BIT - 32), /* 14 -- 16, 0fb3 */ - BMP_C(0x10210000) << (BMP_BIT - 32), /* 15 -- 16, 1021 */ - BMP_C(0x12000000) << (BMP_BIT - 32), /* 16 -- 7, 09 */ - BMP_C(0x15000000) << (BMP_BIT - 32), /* 17 -- 8, 15 */ - BMP_C(0x18000000) << (BMP_BIT - 32), /* 18 -- 6, 06 */ - BMP_C(0x19d3c8d8) << (BMP_BIT - 32), /* 19 -- 31,0ce9e46c */ - BMP_C(0x1c000000) << (BMP_BIT - 32), /* 20 -- 6, 07 */ - BMP_C(0x1d000000) << (BMP_BIT - 32), /* 21 -- 8, 1d */ - BMP_C(0x1d0f0000) << (BMP_BIT - 32), /* 22 -- 16, 1d0f */ - BMP_C(0x1edc6f41) << (BMP_BIT - 32), /* 23 -- 32,1edc6f41 */ - BMP_C(0x1f23b800) << (BMP_BIT - 32), /* 24 -- 24, 1f23b8 */ - BMP_C(0x20140000) << (BMP_BIT - 32), /* 25 -- 14, 0805 */ - BMP_C(0x20b40000) << (BMP_BIT - 32), /* 26 -- 14, 082d */ - BMP_C(0x21890000) << (BMP_BIT - 32), /* 27 -- 16, 2189 */ - BMP_C(0x21cf0200) << (BMP_BIT - 32), /* 28 -- 24, 21cf02 */ - BMP_C(0x25000000) << (BMP_BIT - 32), /* 29 -- 8, 25 */ - BMP_C(0x26b10000) << (BMP_BIT - 32), /* 30 -- 16, 26b1 */ - BMP_C(0x27d00000) << (BMP_BIT - 32), /* 31 -- 13, 04fa */ - BMP_C(0x28000000) << (BMP_BIT - 32), /* 32 -- 5, 05 */ - BMP_C(0x29b10000) << (BMP_BIT - 32), /* 33 -- 16, 29b1 */ - BMP_C(0x30000000) << (BMP_BIT - 32), /* 34 -- 4, 3 */ - BMP_C(0x3010bf7f) << (BMP_BIT - 32), /* 35 -- 32,3010bf7f */ - BMP_C(0x31000000) << (BMP_BIT - 32), /* 36 -- 8, 31 */ - BMP_C(0x31c30000) << (BMP_BIT - 32), /* 37 -- 16, 31c3 */ - BMP_C(0x34000000) << (BMP_BIT - 32), /* 38 -- 6, 0d */ - BMP_C(0x340bc6d9) << (BMP_BIT - 32), /* 39 -- 32,340bc6d9 */ - BMP_C(0x38000000) << (BMP_BIT - 32), /* 40 -- 5, 07 */ - BMP_C(0x39000000) << (BMP_BIT - 32), /* 41 -- 8, 39 */ - BMP_C(0x3d650000) << (BMP_BIT - 32), /* 42 -- 16, 3d65 */ - BMP_C(0x44c20000) << (BMP_BIT - 32), /* 43 -- 16, 44c2 */ - BMP_C(0x48000000) << (BMP_BIT - 32), /* 44 -- 5, 09 */ - BMP_C(0x4acc0000) << (BMP_BIT - 32), /* 45 -- 15, 2566 */ - BMP_C(0x4b370000) << (BMP_BIT - 32), /* 46 -- 16, 4b37 */ - BMP_C(0x4c060000) << (BMP_BIT - 32), /* 47 -- 16, 4c06 */ - BMP_C(0x55000000) << (BMP_BIT - 32), /* 48 -- 8, 55 */ - BMP_C(0x5d6dcb00) << (BMP_BIT - 32), /* 49 -- 24, 5d6dcb */ - BMP_C(0x60000000) << (BMP_BIT - 32), /* 50 -- 3, 3 */ - BMP_C(0x63d00000) << (BMP_BIT - 32), /* 51 -- 16, 63d0 */ - BMP_C(0x64000000) << (BMP_BIT - 32), /* 52 -- 6, 19 */ - BMP_C(0x66400000) << (BMP_BIT - 32), /* 53 -- 10, 199 */ - BMP_C(0x6f910000) << (BMP_BIT - 32), /* 54 -- 16, 6f91 */ - BMP_C(0x70000000) << (BMP_BIT - 32), /* 55 -- 4, 7 */ - BMP_C(0x70a00000) << (BMP_BIT - 32), /* 56 -- 11, 385 */ - BMP_C(0x765e7680) << (BMP_BIT - 32), /* 57 -- 32,765e7680 */ - BMP_C(0x7979bd00) << (BMP_BIT - 32), /* 58 -- 24, 7979bd */ - BMP_C(0x7e000000) << (BMP_BIT - 32), /* 59 -- 8, 7e */ - BMP_C(0x80050000) << (BMP_BIT - 32), /* 60 -- 16, 8005 */ - BMP_C(0x800d0000) << (BMP_BIT - 32), /* 61 -- 16, 800d */ - BMP_C(0x80f00000) << (BMP_BIT - 32), /* 62 -- 12, 80f */ - BMP_C(0x814141ab) << (BMP_BIT - 32), /* 63 -- 32,814141ab */ - BMP_C(0x864cfb00) << (BMP_BIT - 32), /* 64 -- 24, 864cfb */ - BMP_C(0x87315576) << (BMP_BIT - 32), /* 65 -- 32,87315576 */ - BMP_C(0x89ec0000) << (BMP_BIT - 32), /* 66 -- 16, 89ec */ - BMP_C(0x8b320000) << (BMP_BIT - 32), /* 67 -- 15, 4599 */ - BMP_C(0x8bb70000) << (BMP_BIT - 32), /* 68 -- 16, 8bb7 */ - BMP_C(0x8cc00000) << (BMP_BIT - 32), /* 69 -- 10, 233 */ - BMP_C(0x906e0000) << (BMP_BIT - 32), /* 70 -- 16, 906e */ - BMP_C(0x97000000) << (BMP_BIT - 32), /* 71 -- 8, 97 */ - BMP_C(0x98000000) << (BMP_BIT - 32), /* 72 -- 6, 26 */ - BMP_C(0x9b000000) << (BMP_BIT - 32), /* 73 -- 8, 9b */ - BMP_C(0x9c000000) << (BMP_BIT - 32), /* 74 -- 6, 27 */ - BMP_C(0x9e000000) << (BMP_BIT - 32), /* 75 -- 7, 4f */ - BMP_C(0x9ecf0000) << (BMP_BIT - 32), /* 76 -- 16, 9ecf */ - BMP_C(0xa0970000) << (BMP_BIT - 32), /* 77 -- 16, a097 */ - BMP_C(0xa1000000) << (BMP_BIT - 32), /* 78 -- 8, a1 */ - BMP_C(0xa6000000) << (BMP_BIT - 32), /* 79 -- 7, 53 */ - BMP_C(0xa8000000) << (BMP_BIT - 32), /* 80 -- 5, 15 */ - BMP_C(0xa833982b) << (BMP_BIT - 32), /* 81 -- 32,a833982b */ - BMP_C(0xabcdef00) << (BMP_BIT - 32), /* 82 -- 24, abcdef */ - BMP_C(0xb2aa0000) << (BMP_BIT - 32), /* 83 -- 16, b2aa */ - BMP_C(0xb4600000) << (BMP_BIT - 32), /* 84 -- 11, 5a3 */ - BMP_C(0xb4c80000) << (BMP_BIT - 32), /* 85 -- 16, b4c8 */ - BMP_C(0xb704ce00) << (BMP_BIT - 32), /* 86 -- 24, b704ce */ - BMP_C(0xbb3d0000) << (BMP_BIT - 32), /* 87 -- 16, bb3d */ - BMP_C(0xbc000000) << (BMP_BIT - 32), /* 88 -- 8, bc */ - BMP_C(0xbd0be338) << (BMP_BIT - 32), /* 89 -- 32,bd0be338 */ - BMP_C(0xbf050000) << (BMP_BIT - 32), /* 90 -- 16, bf05 */ - BMP_C(0xc0000000) << (BMP_BIT - 32), /* 91 -- 3, 6 */ - BMP_C(0xc2b70000) << (BMP_BIT - 32), /* 92 -- 16, c2b7 */ - BMP_C(0xc6c60000) << (BMP_BIT - 32), /* 93 -- 16, c6c6 */ - BMP_C(0xc8000000) << (BMP_BIT - 32), /* 94 -- 5, 19 */ - BMP_C(0xc8670000) << (BMP_BIT - 32), /* 95 -- 16, c867 */ - BMP_C(0xcbf43926) << (BMP_BIT - 32), /* 96 -- 32,cbf43926 */ - BMP_C(0xd0000000) << (BMP_BIT - 32), /* 97 -- 8, d0 */ - BMP_C(0xd02a0000) << (BMP_BIT - 32), /* 98 -- 15, 6815 */ - BMP_C(0xd0db0000) << (BMP_BIT - 32), /* 99 -- 16, d0db */ - BMP_C(0xd4d00000) << (BMP_BIT - 32), /* 100 -- 12, d4d */ - BMP_C(0xd5000000) << (BMP_BIT - 32), /* 101 -- 8, d5 */ - BMP_C(0xd64e0000) << (BMP_BIT - 32), /* 102 -- 16, d64e */ - BMP_C(0xda000000) << (BMP_BIT - 32), /* 103 -- 8, da */ - BMP_C(0xdaf00000) << (BMP_BIT - 32), /* 104 -- 12, daf */ - BMP_C(0xe0000000) << (BMP_BIT - 32), /* 105 -- 3, 7 */ - BMP_C(0xe3069283) << (BMP_BIT - 32), /* 106 -- 32,e3069283 */ - BMP_C(0xe5cc0000) << (BMP_BIT - 32), /* 107 -- 16, e5cc */ - BMP_C(0xe7a80000) << (BMP_BIT - 32), /* 108 -- 13, 1cf5 */ - BMP_C(0xea000000) << (BMP_BIT - 32), /* 109 -- 7, 75 */ - BMP_C(0xea820000) << (BMP_BIT - 32), /* 110 -- 16, ea82 */ - BMP_C(0xec000000) << (BMP_BIT - 32), /* 111 -- 6, 3b */ - BMP_C(0xf1300000) << (BMP_BIT - 32), /* 112 -- 12, f13 */ - BMP_C(0xf4000000) << (BMP_BIT - 32), /* 113 -- 8, f4 */ - BMP_C(0xf5b00000) << (BMP_BIT - 32), /* 114 -- 12, f5b */ - BMP_C(0xf6400000) << (BMP_BIT - 32), /* 115 -- 10, 3d9 */ - BMP_C(0xf8000000) << (BMP_BIT - 32), /* 116 -- 5, 1f */ - BMP_C(0xfc000000) << (BMP_BIT - 32), /* 117 -- 6, 3f */ - BMP_C(0xfc891918) << (BMP_BIT - 32), /* 118 -- 32,fc891918 */ - BMP_C(0xfd000000) << (BMP_BIT - 32), /* 119 -- 8, fd */ - BMP_C(0xfe000000) << (BMP_BIT - 32), /* 120 -- 7, 7f */ - BMP_C(0xfedcba00) << (BMP_BIT - 32), /* 121 -- 24, fedcba */ - BMP_C(0xfee80000) << (BMP_BIT - 32), /* 122 -- 16, fee8 */ - BMP_C(0xff000000) << (BMP_BIT - 32), /* 123 -- 8, ff */ - BMP_C(0xffc00000) << (BMP_BIT - 32), /* 124 -- 10, 3ff */ - BMP_C(0xfff00000) << (BMP_BIT - 32), /* 125 -- 12, fff */ - BMP_C(0xffff0000) << (BMP_BIT - 32), /* 126 -- 16, ffff */ - BMP_C(0xfffffffe) << (BMP_BIT - 32), /* 127 -- 31,7fffffff */ - BMP_C(0xffffffff) << (BMP_BIT - 32), /* 128 -- 32,ffffffff */ -}; - -/* Table of preset CRC models. - * Sorted by left-justified polynomial for bsearch(). - */ -static const struct mpreset models[] = { - {32UL, b32+ 1, 0, P_BE, 0, b32+ 89, "XFER" }, /* 0 */ - {40UL, b40, 0, P_BE, b40a, b40b, "CRC-40/GSM" }, /* 1 */ - {32UL, b32+ 8, 0, P_BE, b32+128, b32+ 57, "CRC-32/POSIX" }, /* 2 */ - {32UL, b32+ 8, b32+128, P_BE, 0, b32+ 7, "CRC-32/MPEG-2" }, /* 3 */ - {32UL, b32+ 8, b32+128, P_BE, b32+128, b32+118, "CRC-32/BZIP2" }, /* 4 */ - {32UL, b32+ 8, b32+128, P_LE, 0, b32+ 39, "JAMCRC" }, /* 5 */ - {32UL, b32+ 8, b32+128, P_LE, b32+128, b32+ 96, "CRC-32" }, /* 6 */ - {16UL, b32+ 9, 0, P_BE, 0, b32+ 5, "CRC-16/DECT-X" }, /* 7 */ - {16UL, b32+ 9, 0, P_BE, b32+ 2, b32+ 4, "CRC-16/DECT-R" }, /* 8 */ - { 8UL, b32+ 10, 0, P_BE, 0, b32+113, "CRC-8" }, /* 9 */ - { 8UL, b32+ 10, 0, P_BE, b32+ 48, b32+ 78, "CRC-8/ITU" }, /* 10 */ - { 8UL, b32+ 10, b32+123, P_LE, 0, b32+ 97, "CRC-8/ROHC" }, /* 11 */ - {31UL, b32+ 11, b32+127, P_BE, b32+127, b32+ 19, "CRC-31/PHILIPS" }, /* 12 */ - { 6UL, b32+ 13, 0, P_LE, 0, b32+ 18, "CRC-6/ITU" }, /* 13 */ - {82UL, b82, 0, P_LE, 0, b82a, "CRC-82/DARC" }, /* 14 */ - {16UL, b32+ 15, 0, P_BE, 0, b32+ 37, "XMODEM" }, /* 15 */ - {16UL, b32+ 15, 0, P_LE, 0, b32+ 27, "KERMIT" }, /* 16 */ - {16UL, b32+ 15, b32+ 22, P_BE, 0, b32+107, "CRC-16/AUG-CCITT" }, /* 17 */ - {16UL, b32+ 15, b32+ 66, P_LE, 0, b32+ 30, "CRC-16/TMS37157" }, /* 18 */ - {16UL, b32+ 15, b32+ 83, P_LE, 0, b32+ 51, "CRC-16/RIELLO" }, /* 19 */ - {16UL, b32+ 15, b32+ 93, P_LE, 0, b32+ 90, "CRC-A" }, /* 20 */ - {16UL, b32+ 15, b32+126, P_BE, 0, b32+ 33, "CRC-16/CCITT-FALSE"}, /* 21 */ - {16UL, b32+ 15, b32+126, P_BE, b32+126, b32+102, "CRC-16/GENIBUS" }, /* 22 */ - {16UL, b32+ 15, b32+126, P_LE, 0, b32+ 54, "CRC-16/MCRF4XX" }, /* 23 */ - {16UL, b32+ 15, b32+126, P_LE, b32+126, b32+ 70, "X-25" }, /* 24 */ - { 7UL, b32+ 16, 0, P_BE, 0, b32+109, "CRC-7" }, /* 25 */ - { 6UL, b32+ 20, b32+117, P_BE, 0, b32+111, "CRC-6/CDMA2000-B" }, /* 26 */ - { 8UL, b32+ 21, b32+119, P_BE, 0, b32+ 59, "CRC-8/I-CODE" }, /* 27 */ - { 8UL, b32+ 21, b32+123, P_LE, 0, b32+ 71, "CRC-8/EBU" }, /* 28 */ - {32UL, b32+ 23, b32+128, P_LE, b32+128, b32+106, "CRC-32C" }, /* 29 */ - {14UL, b32+ 25, 0, P_LE, 0, b32+ 26, "CRC-14/DARC" }, /* 30 */ - { 5UL, b32+ 32, b32+116, P_LE, b32+116, b32+ 94, "CRC-5/USB" }, /* 31 */ - { 4UL, b32+ 34, 0, P_LE, 0, b32+ 55, "CRC-4/ITU" }, /* 32 */ - { 8UL, b32+ 36, 0, P_LE, 0, b32+ 78, "CRC-8/MAXIM" }, /* 33 */ - { 8UL, b32+ 41, 0, P_LE, 0, b32+ 17, "CRC-8/DARC" }, /* 34 */ - {16UL, b32+ 42, 0, P_BE, b32+126, b32+ 92, "CRC-16/EN-13757" }, /* 35 */ - {16UL, b32+ 42, 0, P_LE, b32+126, b32+110, "CRC-16/DNP" }, /* 36 */ - {64UL, b64, 0, P_BE, 0, b64a, "CRC-64" }, /* 37 */ - {64UL, b64, b64b, P_BE, b64b, b64c, "CRC-64/WE" }, /* 38 */ - {64UL, b64, b64b, P_LE, b64b, b64d, "CRC-64/XZ" }, /* 39 */ - { 5UL, b32+ 44, b32+ 44, P_BE, 0, b32+ 0, "CRC-5/EPC" }, /* 40 */ - {24UL, b32+ 49, b32+ 82, P_BE, 0, b32+ 24, "CRC-24/FLEXRAY-B" }, /* 41 */ - {24UL, b32+ 49, b32+121, P_BE, 0, b32+ 58, "CRC-24/FLEXRAY-A" }, /* 42 */ - { 3UL, b32+ 50, b32+105, P_LE, 0, b32+ 91, "CRC-3/ROHC" }, /* 43 */ - { 6UL, b32+ 52, 0, P_LE, 0, b32+ 72, "CRC-6/DARC" }, /* 44 */ - {11UL, b32+ 56, b32+ 6, P_BE, 0, b32+ 84, "CRC-11" }, /* 45 */ - {16UL, b32+ 60, 0, P_BE, 0, b32+122, "CRC-16/BUYPASS" }, /* 46 */ - {16UL, b32+ 60, 0, P_LE, 0, b32+ 87, "ARC" }, /* 47 */ - {16UL, b32+ 60, 0, P_LE, b32+126, b32+ 43, "CRC-16/MAXIM" }, /* 48 */ - {16UL, b32+ 60, b32+ 61, P_BE, 0, b32+ 76, "CRC-16/DDS-110" }, /* 49 */ - {16UL, b32+ 60, b32+126, P_LE, 0, b32+ 46, "MODBUS" }, /* 50 */ - {16UL, b32+ 60, b32+126, P_LE, b32+126, b32+ 85, "CRC-16/USB" }, /* 51 */ - {12UL, b32+ 62, 0, P_BE, 0, b32+114, "CRC-12/DECT" }, /* 52 */ - {12UL, b32+ 62, 0, P_BELE, 0, b32+104, "CRC-12/3GPP" }, /* 53 */ - {32UL, b32+ 63, 0, P_BE, 0, b32+ 35, "CRC-32Q" }, /* 54 */ - {24UL, b32+ 64, b32+ 86, P_BE, 0, b32+ 28, "CRC-24" }, /* 55 */ - {15UL, b32+ 67, 0, P_BE, 0, b32+ 12, "CRC-15" }, /* 56 */ - {16UL, b32+ 68, 0, P_BE, 0, b32+ 99, "CRC-16/T10-DIF" }, /* 57 */ - {10UL, b32+ 69, 0, P_BE, 0, b32+ 53, "CRC-10" }, /* 58 */ - { 8UL, b32+ 73, 0, P_LE, 0, b32+ 29, "CRC-8/WCDMA" }, /* 59 */ - { 8UL, b32+ 73, b32+123, P_BE, 0, b32+103, "CRC-8/CDMA2000" }, /* 60 */ - { 6UL, b32+ 74, b32+117, P_BE, 0, b32+ 38, "CRC-6/CDMA2000-A" }, /* 61 */ - { 7UL, b32+ 75, b32+120, P_LE, 0, b32+ 79, "CRC-7/ROHC" }, /* 62 */ - {16UL, b32+ 77, 0, P_BE, 0, b32+ 14, "CRC-16/TELEDISK" }, /* 63 */ - { 5UL, b32+ 80, 0, P_LE, 0, b32+ 40, "CRC-5/ITU" }, /* 64 */ - {32UL, b32+ 81, b32+128, P_LE, b32+128, b32+ 65, "CRC-32D" }, /* 65 */ - {16UL, b32+ 95, b32+126, P_BE, 0, b32+ 47, "CRC-16/CDMA2000" }, /* 66 */ - {15UL, b32+ 98, 0, P_BE, b32+ 3, b32+ 45, "CRC-15/MPT1327" }, /* 67 */ - { 8UL, b32+101, 0, P_BE, 0, b32+ 88, "CRC-8/DVB-S2" }, /* 68 */ - {13UL, b32+108, 0, P_BE, 0, b32+ 31, "CRC-13/BBC" }, /* 69 */ - {12UL, b32+112, b32+125, P_BE, 0, b32+100, "CRC-12/CDMA2000" }, /* 70 */ - {10UL, b32+115, b32+124, P_BE, 0, b32+ 69, "CRC-10/CDMA2000" }, /* 71 */ -}; -# define NPRESETS 72 - -/* List of names with pointers to models, pre-sorted for use with bsearch() */ -static const struct malias aliases[] = { - {"ARC", models+47, 1}, /* 0 */ - {"B-CRC-32", models+ 4, 0}, /* 1 */ - {"CKSUM", models+ 2, 0}, /* 2 */ - {"CRC-10", models+58, 1}, /* 3 */ - {"CRC-10/CDMA2000", models+71, 1}, /* 4 */ - {"CRC-11", models+45, 1}, /* 5 */ - {"CRC-12/3GPP", models+53, 1}, /* 6 */ - {"CRC-12/CDMA2000", models+70, 1}, /* 7 */ - {"CRC-12/DECT", models+52, 1}, /* 8 */ - {"CRC-13/BBC", models+69, 1}, /* 9 */ - {"CRC-14/DARC", models+30, 1}, /* 10 */ - {"CRC-15", models+56, 1}, /* 11 */ - {"CRC-15/MPT1327", models+67, 1}, /* 12 */ - {"CRC-16", models+47, 0}, /* 13 */ - {"CRC-16/ACORN", models+15, 0}, /* 14 */ - {"CRC-16/ARC", models+47, 0}, /* 15 */ - {"CRC-16/AUG-CCITT", models+17, 1}, /* 16 */ - {"CRC-16/BUYPASS", models+46, 1}, /* 17 */ - {"CRC-16/CCITT", models+16, 0}, /* 18 */ - {"CRC-16/CCITT-FALSE", models+21, 1}, /* 19 */ - {"CRC-16/CCITT-TRUE", models+16, 0}, /* 20 */ - {"CRC-16/CDMA2000", models+66, 1}, /* 21 */ - {"CRC-16/DARC", models+22, 0}, /* 22 */ - {"CRC-16/DDS-110", models+49, 1}, /* 23 */ - {"CRC-16/DECT-R", models+ 8, 1}, /* 24 */ - {"CRC-16/DECT-X", models+ 7, 1}, /* 25 */ - {"CRC-16/DNP", models+36, 1}, /* 26 */ - {"CRC-16/EN-13757", models+35, 1}, /* 27 */ - {"CRC-16/EPC", models+22, 0}, /* 28 */ - {"CRC-16/GENIBUS", models+22, 1}, /* 29 */ - {"CRC-16/I-CODE", models+22, 0}, /* 30 */ - {"CRC-16/IBM-SDLC", models+24, 0}, /* 31 */ - {"CRC-16/ISO-HDLC", models+24, 0}, /* 32 */ - {"CRC-16/LHA", models+47, 0}, /* 33 */ - {"CRC-16/MAXIM", models+48, 1}, /* 34 */ - {"CRC-16/MCRF4XX", models+23, 1}, /* 35 */ - {"CRC-16/RIELLO", models+19, 1}, /* 36 */ - {"CRC-16/SPI-FUJITSU", models+17, 0}, /* 37 */ - {"CRC-16/T10-DIF", models+57, 1}, /* 38 */ - {"CRC-16/TELEDISK", models+63, 1}, /* 39 */ - {"CRC-16/TMS37157", models+18, 1}, /* 40 */ - {"CRC-16/USB", models+51, 1}, /* 41 */ - {"CRC-16/VERIFONE", models+46, 0}, /* 42 */ - {"CRC-24", models+55, 1}, /* 43 */ - {"CRC-24/FLEXRAY-A", models+42, 1}, /* 44 */ - {"CRC-24/FLEXRAY-B", models+41, 1}, /* 45 */ - {"CRC-24/OPENPGP", models+55, 0}, /* 46 */ - {"CRC-3/ROHC", models+43, 1}, /* 47 */ - {"CRC-31/PHILIPS", models+12, 1}, /* 48 */ - {"CRC-32", models+ 6, 1}, /* 49 */ - {"CRC-32/AAL5", models+ 4, 0}, /* 50 */ - {"CRC-32/ADCCP", models+ 6, 0}, /* 51 */ - {"CRC-32/BZIP2", models+ 4, 1}, /* 52 */ - {"CRC-32/CASTAGNOLI", models+29, 0}, /* 53 */ - {"CRC-32/DECT-B", models+ 4, 0}, /* 54 */ - {"CRC-32/ISCSI", models+29, 0}, /* 55 */ - {"CRC-32/MPEG-2", models+ 3, 1}, /* 56 */ - {"CRC-32/POSIX", models+ 2, 1}, /* 57 */ - {"CRC-32C", models+29, 1}, /* 58 */ - {"CRC-32D", models+65, 1}, /* 59 */ - {"CRC-32Q", models+54, 1}, /* 60 */ - {"CRC-4/ITU", models+32, 1}, /* 61 */ - {"CRC-40/GSM", models+ 1, 1}, /* 62 */ - {"CRC-5/EPC", models+40, 1}, /* 63 */ - {"CRC-5/ITU", models+64, 1}, /* 64 */ - {"CRC-5/USB", models+31, 1}, /* 65 */ - {"CRC-6/CDMA2000-A", models+61, 1}, /* 66 */ - {"CRC-6/CDMA2000-B", models+26, 1}, /* 67 */ - {"CRC-6/DARC", models+44, 1}, /* 68 */ - {"CRC-6/ITU", models+13, 1}, /* 69 */ - {"CRC-64", models+37, 1}, /* 70 */ - {"CRC-64/WE", models+38, 1}, /* 71 */ - {"CRC-64/XZ", models+39, 1}, /* 72 */ - {"CRC-7", models+25, 1}, /* 73 */ - {"CRC-7/ROHC", models+62, 1}, /* 74 */ - {"CRC-8", models+ 9, 1}, /* 75 */ - {"CRC-8/AES", models+28, 0}, /* 76 */ - {"CRC-8/CDMA2000", models+60, 1}, /* 77 */ - {"CRC-8/DARC", models+34, 1}, /* 78 */ - {"CRC-8/DVB-S2", models+68, 1}, /* 79 */ - {"CRC-8/EBU", models+28, 1}, /* 80 */ - {"CRC-8/I-CODE", models+27, 1}, /* 81 */ - {"CRC-8/ITU", models+10, 1}, /* 82 */ - {"CRC-8/MAXIM", models+33, 1}, /* 83 */ - {"CRC-8/ROHC", models+11, 1}, /* 84 */ - {"CRC-8/WCDMA", models+59, 1}, /* 85 */ - {"CRC-82/DARC", models+14, 1}, /* 86 */ - {"CRC-A", models+20, 1}, /* 87 */ - {"CRC-B", models+24, 0}, /* 88 */ - {"CRC-CCITT", models+16, 0}, /* 89 */ - {"CRC-IBM", models+47, 0}, /* 90 */ - {"DOW-CRC", models+33, 0}, /* 91 */ - {"JAMCRC", models+ 5, 1}, /* 92 */ - {"KERMIT", models+16, 1}, /* 93 */ - {"MODBUS", models+50, 1}, /* 94 */ - {"PKZIP", models+ 6, 0}, /* 95 */ - {"R-CRC-16", models+ 8, 0}, /* 96 */ - {"X-25", models+24, 1}, /* 97 */ - {"X-CRC-12", models+52, 0}, /* 98 */ - {"X-CRC-16", models+ 7, 0}, /* 99 */ - {"XFER", models+ 0, 1}, /* 100 */ - {"XMODEM", models+15, 1}, /* 101 */ - {"ZMODEM", models+15, 0}, /* 102 */ - {NULL, NULL, 0}, /* terminating entry */ -}; -# define NALIASES 103 - -# endif /* BMP_BIT */ -#else /* PRESETS */ - -static const struct mpreset models[] = { - { 0UL, 0, 0, P_BE, 0, 0, NULL }, /* terminating entry */ -}; -# define NPRESETS 0 - -static const struct malias aliases[] = { - {NULL, NULL, 0}, /* terminating entry */ -}; -# define NALIASES 0 - -#endif /* PRESETS */ - -static const poly_t pzero = PZERO; - -static int acmp(const struct malias *, const struct malias *); -static void munpack(model_t *, const struct mpreset *); - -/* copy a parameter of a preset into a model */ -#define MUNPACK(parm) \ - praloc(&dest->parm, (src->b##parm ? src->width : 0UL)); \ - for(iter=0UL, idx=0UL; iter < dest->parm.length; iter += BMP_BIT, ++idx)\ - dest->parm.bitmap[idx] = src->b##parm[idx]; - -/* Definitions */ - -void -mcpy(model_t *dest, const model_t *src) { - /* Copies the parameters of src to dest. - * dest must be an initialised model. - */ - if(!dest || !src) return; - pcpy(&dest->spoly, src->spoly); - pcpy(&dest->init, src->init); - pcpy(&dest->xorout, src->xorout); - pcpy(&dest->check, src->check); - dest->flags = src->flags; - /* link to the name as it is static */ - dest->name = src->name; -} - -void -mfree(model_t *model) { - /* Frees the parameters of model. */ - if(!model) return; - pfree(&model->spoly); - pfree(&model->init); - pfree(&model->xorout); - pfree(&model->check); - /* not name as it is static */ - /* not model either, it might point to an array! */ -} - -int -mcmp(const model_t *a, const model_t *b) { - /* Compares a and b for identical effect, i.e. disregarding - * trailing zeroes in parameter polys. - * Intended for bsearch() to find a matching model in models[]. - */ - int result; - if(!a || !b) return(!b - !a); - if((result = psncmp(&a->spoly, &b->spoly))) return(result); - if((result = psncmp(&a->init, &b->init))) return(result); - if((a->flags & P_REFIN) && (~b->flags & P_REFIN)) return(1); - if((~a->flags & P_REFIN) && (b->flags & P_REFIN)) return(-1); - if((a->flags & P_REFOUT) && (~b->flags & P_REFOUT)) return(1); - if((~a->flags & P_REFOUT) && (b->flags & P_REFOUT)) return(-1); - return(psncmp(&a->xorout, &b->xorout)); -} - -int -mbynam(model_t *dest, const char *key) { - /* Sets parameters in dest according to the model named by key. - */ - struct malias akey = {NULL, NULL, 0}, *aptr; - char *ukey, *uptr; - - if(!aliases->name) - return(-1); - if(!(ukey = malloc((size_t) 1 + strlen(key)))) - uerror("cannot allocate memory for comparison string"); - akey.name = uptr = ukey; - do - *uptr++ = toupper(*key); - while(*key++); - - aptr = bsearch(&akey, aliases, NALIASES, sizeof(struct malias), (int (*)(const void *, const void *)) &acmp); - free(ukey); - - if(aptr == NULL) - return(0); - munpack(dest, aptr->model); - return(1); -} - -void -mbynum(model_t *dest, int num) { - /* Sets parameters in dest according to the model indexed by num. */ - if(num > NPRESETS) - num = NPRESETS; - munpack(dest, models+num); -} - -int -mcount(void) { - /* Returns the number of preset models. */ - return(NPRESETS); -} - -char * -mnames(void) { - /* Returns a malloc()-ed string of the names of all preset - * models, separated by newlines and terminated by NULL. - * Aliases are not listed. - */ - size_t size = 0; - char *string, *sptr; - const struct malias *aptr = aliases; - - while(aptr->name) { - if(aptr->isprimry) - size += strlen(aptr->name) + 1; - ++aptr; - } - if(!size) return(NULL); - if((string = malloc(size))) { - aptr = aliases; - sptr = string; - while(aptr->name) { - if(aptr->isprimry) { - strcpy(sptr, aptr->name); - sptr += strlen(aptr->name); - *sptr++ = '\n'; - } - ++aptr; - } - *--sptr = '\0'; - } else - uerror("cannot allocate memory for list of models"); - - return(string); -} - -char * -mtostr(const model_t *model) { - /* Returns a malloc()-ed string containing a Williams model - * record representing the input model. - * mcanon() should be called on the argument before printing. - */ - size_t size; - char *polystr, *initstr, *xorotstr, *checkstr, strbuf[512], *string = NULL; - - if(!model) return(NULL); - polystr = ptostr(model->spoly, P_RTJUST, 4); - initstr = ptostr(model->init, P_RTJUST, 4); - xorotstr = ptostr(model->xorout, P_RTJUST, 4); - checkstr = ptostr(model->check, P_RTJUST, 4); - - sprintf(strbuf, "%lu", plen(model->spoly)); - size = - 70 - + (model->name && *model->name ? 2 + strlen(model->name) : 6) - + strlen(strbuf) - + (polystr && *polystr ? strlen(polystr) : 6) - + (initstr && *initstr ? strlen(initstr) : 6) - + (model->flags & P_REFIN ? 4 : 5) - + (model->flags & P_REFOUT ? 4 : 5) - + (xorotstr && *xorotstr ? strlen(xorotstr) : 6) - + (checkstr && *checkstr ? strlen(checkstr) : 6); - if((string = malloc(size))) { - sprintf(strbuf, "\"%s\"", model->name); - sprintf(string, - "width=%lu " - "poly=0x%s " - "init=0x%s " - "refin=%s " - "refout=%s " - "xorout=0x%s " - "check=0x%s " - "name=%s", - plen(model->spoly), - polystr && *polystr ? polystr : "(none)", - initstr && *initstr ? initstr : "(none)", - (model->flags & P_REFIN) ? "true" : "false", - (model->flags & P_REFOUT) ? "true" : "false", - xorotstr && *xorotstr ? xorotstr : "(none)", - checkstr && *checkstr ? checkstr : "(none)", - (model->name && *model->name) ? strbuf : "(none)"); - } - free(polystr); - free(initstr); - free(xorotstr); - free(checkstr); - if(!string) - uerror("cannot allocate memory for model description"); - return(string); -} - -void -mmatch(model_t *model, int flags) { - /* searches models[] for a model matching the argument, and links a name if found - * if flags & M_OVERWR, copies the found model onto the argument. */ - model_t *mptr; - if(!model) return; - - mptr = bsearch(model, models, NPRESETS, sizeof(model_t), (int (*)(const void *, const void *)) &mcmp); - if(mptr) { - model->name = mptr->name; - if(flags & M_OVERWR) - mcpy(model, mptr); - } -} - -void -mcanon(model_t *model) { - /* canonicalise a model */ - unsigned long dlen; - - if(!model) return; - - /* extending on the right here. This preserves the functionality - * of a presumed working model. - */ - psnorm(&model->spoly); - dlen = plen(model->spoly); - praloc(&model->init, dlen); - praloc(&model->xorout, dlen); - - if(!plen(model->check)) - mcheck(model); -} - -void -mcheck(model_t *model) { - /* calculate a check for the model */ - poly_t checkstr, check; - - /* generate the check string with the correct bit order */ - checkstr = strtop("313233343536373839", model->flags, 8); - check = pcrc(checkstr, model->spoly, model->init, pzero, model->flags); - if(model->flags & P_REFOUT) - prev(&check); - psum(&check, model->xorout, 0UL); - model->check = check; - pfree(&checkstr); -} - -void -mrev(model_t *model) { - /* reverse the model to calculate reversed CRCs */ - /* Here we invert RefIn and RefOut so that the user need only - * reverse the order of characters in the arguments, not the - * characters themselves. If RefOut=True, the mirror image of - * Init seen through RefOut becomes XorOut, and as RefOut - * becomes false, the XorOut value moved to Init stays upright. - * If RefOut=False, Init transfers to XorOut without reflection - * but the new Init must be reflected to present the same image, - * as RefOut becomes true. - */ - poly_t temp; - - prcp(&model->spoly); - if(model->flags & P_REFOUT) - prev(&model->init); - else - prev(&model->xorout); - - /* exchange init and xorout */ - temp = model->init; - model->init = model->xorout; - model->xorout = temp; - - /* invert refin and refout */ - model->flags ^= P_REFIN | P_REFOUT; - - mnovel(model); -} - -void -mnovel(model_t *model) { - /* remove name and check string from modified model */ - model->name = NULL; - pfree(&model->check); -} - -static int -acmp(const struct malias *a, const struct malias *b) { - /* compares two aliases, for use in bsearch */ - if(!a || !b) return(!b - !a); - if(!a->name || !b->name) return(!b->name - !a->name); - return(strcmp(a->name, b->name)); -} - -static void -munpack(model_t *dest, const struct mpreset *src) { - /* Copies the parameters of src to dest. - * dest must be an initialised model. - */ - unsigned long iter, idx; - if(!dest || !src) return; - MUNPACK(spoly); - MUNPACK(init); - MUNPACK(xorout); - MUNPACK(check); - dest->flags = src->flags; - /* link to the name as it is static */ - dest->name = src->name; -} diff --git a/client/reveng/poly.c b/client/reveng/poly.c deleted file mode 100644 index e4a8e8f9..00000000 --- a/client/reveng/poly.c +++ /dev/null @@ -1,1196 +0,0 @@ -/* poly.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng 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. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -/* 2015-04-03: added direct mode to strtop() - * 2014-01-11: added LOFS(), RNDUP() - * 2013-09-16: SIZE(), IDX(), OFS() macros bitshift if BMP_POF2 - * 2013-02-07: conditional non-2^n fix, pmpar() return mask constant type - * 2013-01-17: fixed pfirst(), plast() for non-2^n BMP_BIT - * 2012-07-16: added pident() - * 2012-05-23: added pmpar() - * 2012-03-03: internal lookup tables stored better - * 2012-03-02: fixed full-width masking in filtop() - * 2011-09-06: added prevch() - * 2011-08-27: fixed zero test in piter() - * 2011-01-17: fixed ANSI C warnings, uses bmp_t type - * 2011-01-15: palloc() and praloc() gracefully handle lengths slightly - * less than ULONG_MAX - * 2011-01-15: strtop() error on invalid argument. pkchop() special case - * when argument all zeroes - * 2011-01-14: added pkchop() - * 2011-01-04: fixed bogus final length calculation in wide pcrc() - * 2011-01-02: faster, more robust prcp() - * 2011-01-01: commented functions, full const declarations, all-LUT rev() - * 2010-12-26: renamed CRC RevEng - * 2010-12-18: removed pmods(), finished pcrc(), added piter() - * 2010-12-17: roughed out pcrc(). difficult, etiam aberat musa heri :( - * 2010-12-15: added psnorm(), psncmp(); optimised pnorm(); fix to praloc() - * 2010-12-14: strtop() resets count between passes - * 2010-12-12: added pright() - * 2010-12-11: filtop won't read more than length bits - * 2010-12-10: finished filtop. 26 public functions - * 2010-12-05: finished strtop, pxsubs; unit tests - * 2010-12-02: project started - */ - -/* Note: WELL-FORMED poly_t objects have a valid bitmap pointer pointing - * to a malloc()-ed array of at least as many bits as stated in its - * length field. Any poly_t with a length of 0 is also a WELL-FORMED - * poly_t (whatever value the bitmap pointer has.) - * All poly_t objects passed to and from functions must be WELL-FORMED - * unless otherwise stated. - * - * CLEAN (or CANONICAL) poly_t objects are WELL-FORMED objects in which - * all spare bits in the bitmap word containing the last bit are zero. - * (Any excess allocated words will not be accessed.) - * - * SEMI-NORMALISED poly_t objects are CLEAN objects in which the last - * bit, at position (length - 1), is one. - * - * NORMALISED poly_t objects are SEMI-NORMALISED objects in which the - * first bit is one. - * - * pfree() should be called on every poly_t object (including - * those returned by functions) after its last use. - * As always, free() should be called on every malloc()-ed string after - * its last use. - */ - -#include -#include -#include -#include "reveng.h" - -static bmp_t getwrd(const poly_t poly, unsigned long iter); -static bmp_t rev(bmp_t accu, int bits); -static void prhex(char **spp, bmp_t bits, int flags, int bperhx); - -static const poly_t pzero = PZERO; - -/* word number (0..m-1) of var'th bit (0..n-1) */ -#if BMP_POF2 >= 5 -# define IDX(var) ((var) >> BMP_POF2) -#else -# define IDX(var) ((var) / BMP_BIT) -#endif - -/* size of polynomial with var bits */ -#if BMP_POF2 >= 5 -# define SIZE(var) ((BMP_BIT - 1UL + (var)) >> BMP_POF2) -#else -# define SIZE(var) ((BMP_BIT - 1UL + (var)) / BMP_BIT) -#endif - -/* polynomial length rounded up to BMP_BIT */ -#ifdef BMP_POF2 -# define RNDUP(var) (~(BMP_BIT - 1UL) & (BMP_BIT - 1UL + (var))) -#else -# define RNDUP(var) ((BMP_BIT - (var) % BMP_BIT) % BMP_BIT + (var)) -#endif - -/* bit offset (0..BMP_BIT-1, 0 = LSB) of var'th bit (0..n-1) */ -#ifdef BMP_POF2 -# define OFS(var) ((int) ((BMP_BIT - 1UL) & ~(var))) -#else -# define OFS(var) ((int) (BMP_BIT - 1UL - (var) % BMP_BIT)) -#endif - -/* bit offset (0..BMP_BIT-1, 0 = MSB) of var'th bit (0..n-1) */ -#ifdef BMP_POF2 -# define LOFS(var) ((int) ((BMP_BIT - 1UL) & (var))) -#else -# define LOFS(var) ((int) ((var) % BMP_BIT)) -#endif - -poly_t -filtop(FILE *input, unsigned long length, int flags, int bperhx) { - /* reads binary data from input into a poly_t until EOF or until - * length bits are read. Characters are read until - * ceil(bperhx / CHAR_BIT) bits are collected; if P_LTLBYT is - * set in flags then the first character contains the LSB, - * otherwise the last one does. The least significant bperhx - * bits are taken, reflected (if P_REFIN) and appended to the - * result, then more characters are read. The maximum number of - * characters read is - * floor(length / bperhx) * ceil(bperhx / * CHAR_BIT). - * The returned poly_t is CLEAN. - */ - - bmp_t accu = BMP_C(0); - bmp_t mask = bperhx == BMP_BIT ? ~BMP_C(0) : (BMP_C(1) << bperhx) - BMP_C(1); - unsigned long iter = 0UL, idx; - int cmask = ~(~0U << CHAR_BIT), c; - int count = 0, ofs; - poly_t poly = PZERO; - if(bperhx == 0) return(poly); - - length -= length % bperhx; - palloc(&poly, length); /* >= 0 */ - - while(iter < length && (c = fgetc(input)) != EOF) { - if(flags & P_LTLBYT) - accu |= (bmp_t) (c & cmask) << count; - else - accu = (accu << CHAR_BIT) | (bmp_t) (c & cmask); - count += CHAR_BIT; - if(count >= bperhx) { - /* the low bperhx bits of accu contain bits of the poly.*/ - iter += bperhx; - count = 0; - if(flags & P_REFIN) - accu = rev(accu, bperhx); - accu &= mask; - - /* iter >= bperhx > 0 */ - idx = IDX(iter - 1UL); - ofs = OFS(iter - 1UL); - poly.bitmap[idx] |= accu << ofs; - if(ofs + bperhx > BMP_BIT) { - poly.bitmap[idx-1] |= accu >> (BMP_BIT - ofs); - } - accu = BMP_C(0); /* only needed for P_LTLBYT */ - } - } - praloc(&poly, iter); - return(poly); -} - -poly_t -strtop(const char *string, int flags, int bperhx) { - /* Converts a hex or character string to a poly_t. - * Each character is converted to a hex nibble yielding 4 bits - * unless P_DIRECT, when each character yields CHAR_BIT bits. - * Nibbles and characters are accumulated left-to-right - * unless P_DIRECT && P_LTLBYT, when they are accumulated - * right-to-left without reflection. - * As soon as at least bperhx bits are accumulated, the - * rightmost bperhx bits are reflected (if P_REFIN) - * and appended to the poly. When !P_DIRECT: - * bperhx=8 reads hex nibbles in pairs - * bperhx=7 reads hex nibbles in pairs and discards - * b3 of first nibble - * bperhx=4 reads hex nibbles singly - * bperhx=3 reads octal - * bperhx=1 reads longhand binary - * in theory if !P_REFIN, bperhx can be any multiple of 4 - * with equal effect - * The returned poly_t is CLEAN. - */ - - /* make two passes, one to determine the poly size - * one to populate the bitmap - */ - unsigned long length = 1UL, idx; - bmp_t accu; - bmp_t mask = bperhx == BMP_BIT ? ~BMP_C(0) : (BMP_C(1) << bperhx) - BMP_C(1); - int pass, count, ofs; - int cmask = ~(~0U << CHAR_BIT), c; - const char *s; - - poly_t poly = PZERO; - if(bperhx > BMP_BIT || bperhx <= 0 || string == NULL || *string == '\0') - return(poly); - - for(pass=0; pass<2 && length > 0UL; ++pass) { - s = string; - length = 0UL; - count = 0; - accu = BMP_C(0); - while((c = *s++)) { - if(flags & P_DIRECT) { - if(flags & P_LTLBYT) - accu |= (bmp_t) (c & cmask) << count; - else - accu = (accu << CHAR_BIT) | (bmp_t) (c & cmask); - count += CHAR_BIT; - } else { - if(c == ' ' || c == '\t' || c == '\r' || c == '\n') continue; - accu <<= 4; - count += 4; - switch(c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - accu |= (bmp_t) c - '0'; - break; - case 'A': - case 'a': - accu |= BMP_C(0xa); - break; - case 'B': - case 'b': - accu |= BMP_C(0xb); - break; - case 'C': - case 'c': - accu |= BMP_C(0xc); - break; - case 'D': - case 'd': - accu |= BMP_C(0xd); - break; - case 'E': - case 'e': - accu |= BMP_C(0xe); - break; - case 'F': - case 'f': - accu |= BMP_C(0xf); - break; - default: - uerror("invalid character in hexadecimal argument"); - } - } - - if(count >= bperhx) { - /* the low bperhx bits of accu contain bits of the poly. - * in pass 0, increment length by bperhx. - * in pass 1, put the low bits of accu into the bitmap. */ - length += bperhx; - count = 0; - if(pass == 1) { - if(flags & P_REFIN) - accu = rev(accu, bperhx); - accu &= mask; - - /* length >= bperhx > 0 */ - idx = IDX(length - 1); - ofs = OFS(length - 1); - poly.bitmap[idx] |= accu << ofs; - if(ofs + bperhx > BMP_BIT) - poly.bitmap[idx-1] |= accu >> (BMP_BIT - ofs); - accu = BMP_C(0); /* only needed for P_LTLBYT */ - } - } - } - if(pass == 0) palloc(&poly, length); - } - return(poly); -} - -char * -ptostr(const poly_t poly, int flags, int bperhx) { - /* Returns a malloc()-ed string containing a hexadecimal - * representation of poly. See phxsubs(). - */ - return(pxsubs(poly, flags, bperhx, 0UL, poly.length)); -} - -char * -pxsubs(const poly_t poly, int flags, int bperhx, unsigned long start, unsigned long end) { - /* Returns a malloc()-ed string containing a hexadecimal - * representation of a portion of poly, from bit offset start to - * (end - 1) inclusive. The output is grouped into words of - * bperhx bits each. If P_RTJUST then the first word is padded - * with zeroes at the MSB end to make a whole number of words, - * otherwise the last word is padded at the LSB end. After - * justification the bperhx bits of each word are reversed (if - * P_REFOUT) and printed as a hex sequence, with words - * optionally separated by spaces (P_SPACE). - * If end exceeds the length of poly then zero bits are appended - * to make up the difference, in which case poly must be CLEAN. - */ - char *string, *sptr; - unsigned long size, iter; - bmp_t accu; - bmp_t mask = bperhx == BMP_BIT ? ~BMP_C(0) : (BMP_C(1) << bperhx) - BMP_C(1); - int cperhx, part; - - if(bperhx <= 0 || bperhx > BMP_BIT) return(NULL); - - if(start > poly.length) start = poly.length; - if(end > poly.length) end = poly.length; - if(end < start) end = start; - - cperhx = (bperhx + 3) >> 2; - if(flags & P_SPACE) ++cperhx; - - size = (end - start + bperhx - 1UL) / bperhx; - size *= cperhx; - if(!size || ~flags & P_SPACE) ++size; /* for trailing null */ - - if(!(sptr = string = (char *) malloc(size))) - uerror("cannot allocate memory for string"); - - size = end - start; - part = (int) size % bperhx; - if(part && flags & P_RTJUST) { - iter = start + part; - accu = getwrd(poly, iter - 1UL) & ((BMP_C(1) << part) - BMP_C(1)); - if(flags & P_REFOUT) - /* best to reverse over bperhx rather than part, I think - * e.g. converting a 7-bit poly to 8-bit little-endian hex - */ - accu = rev(accu, bperhx); - prhex(&sptr, accu, flags, bperhx); - if(flags & P_SPACE && size > iter) *sptr++ = ' '; - } else { - iter = start; - } - - while((iter+=bperhx) <= end) { - accu = getwrd(poly, iter - 1UL) & mask; - if(flags & P_REFOUT) - accu = rev(accu, bperhx); - prhex(&sptr, accu, flags, bperhx); - if(flags & P_SPACE && size > iter) *sptr++ = ' '; - } - - if(part && ~flags & P_RTJUST) { - accu = getwrd(poly, end - 1UL); - if(flags & P_REFOUT) - accu = rev(accu, part); - else - accu = accu << (bperhx - part) & mask; - prhex(&sptr, accu, flags, bperhx); - } - *sptr = '\0'; - return(string); -} - -poly_t -pclone(const poly_t poly) { - /* Returns a freestanding copy of poly. Does not clean poly or - * the result. - */ - poly_t clone = PZERO; - - pcpy(&clone, poly); - return(clone); -} - -void -pcpy(poly_t *dest, const poly_t src) { - /* Assigns (copies) src into dest. Does not clean src or dest. - */ - unsigned long iter, idx; - - praloc(dest, src.length); - for(iter=0UL, idx=0UL; iter < src.length; iter += BMP_BIT, ++idx) - dest->bitmap[idx] = src.bitmap[idx]; -} - -void -pcanon(poly_t *poly) { - /* Converts poly into a CLEAN object by freeing unused bitmap words - * and clearing any bits in the last word beyond the last bit. - * The length field has absolute priority over the contents of the bitmap. - * Canonicalisation differs from normalisation in that leading and trailing - * zero terms are significant and preserved. - * poly may or may not be WELL-FORMED. - */ - praloc(poly, poly->length); -} - -void -pnorm(poly_t *poly) { - /* Converts poly into a NORMALISED object by removing leading - * and trailing zeroes, so that the polynomial starts and ends - * with significant terms. - * poly may or may not be WELL-FORMED. - */ - unsigned long first; - - /* call pcanon() here so pfirst() and plast() return the correct - * results - */ - pcanon(poly); - first = pfirst(*poly); - if(first) - pshift(poly, *poly, 0UL, first, plast(*poly), 0UL); - else - praloc(poly, plast(*poly)); -} - -void -psnorm(poly_t *poly) { - /* Converts poly into a SEMI-NORMALISED object by removing - * trailing zeroes, so that the polynomial ends with a - * significant term. - * poly may or may not be WELL-FORMED. - */ - - /* call pcanon() here so plast() returns the correct result */ - pcanon(poly); - praloc(poly, plast(*poly)); -} - -void -pchop(poly_t *poly) { - /* Normalise poly, then chop off the highest significant term - * (produces a SEMI-NORMALISED object). poly becomes a suitable - * divisor for pcrc(). - * poly may or may not be WELL-FORMED. - */ - - /* call pcanon() here so pfirst() and plast() return correct - * results - */ - pcanon(poly); - pshift(poly, *poly, 0UL, pfirst(*poly) + 1UL, plast(*poly), 0UL); -} - -void -pkchop(poly_t *poly) { - /* Convert poly from Koopman notation to chopped form (produces - * a SEMI-NORMALISED object). poly becomes a suitable divisor - * for pcrc(). - * poly may or may not be WELL-FORMED. - */ - unsigned long first; - - /* call pcanon() here so pfirst() returns the correct result */ - pcanon(poly); - first = pfirst(*poly); - if(first >= poly->length) { - pfree(poly); - return; - } - pshift(poly, *poly, 0UL, first + 1UL, poly->length, 1UL); - piter(poly); -} - -unsigned long -plen(const poly_t poly) { - /* Return length of polynomial. - * poly may or may not be WELL-FORMED. - */ - return(poly.length); -} - -int -pcmp(const poly_t *a, const poly_t *b) { - /* Compares poly_t objects for identical sizes and contents. - * a and b must be CLEAN. - * Defines a total order relation for sorting, etc. although - * mathematically, polynomials of equal degree are no greater or - * less than one another. - */ - unsigned long iter; - bmp_t *aptr, *bptr; - - if(!a || !b) return(!b - !a); - if(a->length < b->length) return(-1); - if(a->length > b->length) return(1); - aptr = a->bitmap; - bptr = b->bitmap; - for(iter=0UL; iter < a->length; iter += BMP_BIT) { - if(*aptr < *bptr) - return(-1); - if(*aptr++ > *bptr++) - return(1); - } - return(0); -} - -int -psncmp(const poly_t *a, const poly_t *b) { - /* Compares polys for identical effect, i.e. as though the - * shorter poly were padded with zeroes to the length of the - * longer. - * a and b must still be CLEAN, therefore psncmp() is *not* - * identical to pcmp() on semi-normalised polys as psnorm() - * clears the slack space. - */ - unsigned long length, iter, idx; - bmp_t aword, bword; - if(!a || !b) return(!b - !a); - length = (a->length > b->length) ? a->length : b->length; - for(iter = 0UL, idx = 0UL; iter < length; iter += BMP_BIT, ++idx) { - aword = (iter < a->length) ? a->bitmap[idx] : BMP_C(0); - bword = (iter < b->length) ? b->bitmap[idx] : BMP_C(0); - if(aword < bword) - return(-1); - if(aword > bword) - return(1); - } - return(0); -} - - -int -ptst(const poly_t poly) { - /* Tests whether a polynomial equals zero. Returns 0 if equal, - * a nonzero value otherwise. - * poly must be CLEAN. - */ - unsigned long iter; - bmp_t *bptr; - if(!poly.bitmap) return(0); - for(iter = 0UL, bptr = poly.bitmap; iter < poly.length; iter += BMP_BIT) - if(*bptr++) return(1); - return(0); -} - -unsigned long -pfirst(const poly_t poly) { - /* Returns the index of the first nonzero term in poly. If none - * is found, returns the length of poly. - * poly must be CLEAN. - */ - unsigned long idx = 0UL, size = SIZE(poly.length); - bmp_t accu = BMP_C(0); /* initialiser for Acorn C */ - unsigned int probe = BMP_SUB, ofs = 0; - - while(idx < size && !(accu = poly.bitmap[idx])) ++idx; - if(idx >= size) return(poly.length); - while(probe) { -#ifndef BMP_POF2 - while((ofs | probe) >= (unsigned int) BMP_BIT) probe >>= 1; -#endif - if(accu >> (ofs | probe)) ofs |= probe; - probe >>= 1; - } - - return(BMP_BIT - 1UL - ofs + idx * BMP_BIT); -} - -unsigned long -plast(const poly_t poly) { - /* Returns 1 plus the index of the last nonzero term in poly. - * If none is found, returns zero. - * poly must be CLEAN. - */ - unsigned long idx, size = SIZE(poly.length); - bmp_t accu; - unsigned int probe = BMP_SUB, ofs = 0; - - if(!poly.length) return(0UL); - idx = size - 1UL; - while(idx && !(accu = poly.bitmap[idx])) --idx; - if(!idx && !(accu = poly.bitmap[idx])) return(0UL); - /* now accu == poly.bitmap[idx] and contains last significant term */ - while(probe) { -#ifndef BMP_POF2 - while((ofs | probe) >= (unsigned int) BMP_BIT) probe >>= 1; -#endif - if(accu << (ofs | probe)) ofs |= probe; - probe >>= 1; - } - - return(idx * BMP_BIT + ofs + 1UL); -} - -poly_t -psubs(const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail) { - poly_t dest = PZERO; - pshift(&dest, src, head, start, end, tail); - return(dest); -} - -void -pright(poly_t *poly, unsigned long length) { - /* Trims or extends poly to length at the left edge, prepending - * zeroes if necessary. Analogous to praloc() except the - * rightmost terms of poly are preserved. - * On entry, poly may or may not be WELL-FORMED. - * On exit, poly is CLEAN. - */ - - if(length > poly->length) - pshift(poly, *poly, length - poly->length, 0UL, poly->length, 0UL); - else if(length < poly->length) - pshift(poly, *poly, 0UL, poly->length - length, poly->length, 0UL); - else - praloc(poly, poly->length); -} - -void -pshift(poly_t *dest, const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail) { - /* copies bits start to end-1 of src to dest, plus the number of leading and trailing zeroes given by head and tail. - * end may exceed the length of src in which case more zeroes are appended. - * dest may point to src, in which case the poly is edited in place. - * On exit, dest is CLEAN. - */ - - unsigned long length, fulllength, size, fullsize, iter, idx, datidx; - /* condition inputs; end, head and tail may be any value */ - if(end < start) end = start; - - length = end - start + head; - fulllength = length + tail; - if(fulllength > src.length) - praloc(dest, fulllength); - else - praloc(dest, src.length); - - /* number of words in new poly */ - size = SIZE(length); - fullsize = SIZE(fulllength); - /* array index of first word ending up with source material */ - datidx = IDX(head); - - if(head > start && end > start) { - /* shifting right, size > 0 */ - /* index of the source bit ending up in the LSB of the last word - * size * BMP_BIT >= length > head > 0 */ - iter = size * BMP_BIT - head - 1UL; - for(idx = size - 1UL; idx > datidx; iter -= BMP_BIT, --idx) - dest->bitmap[idx] = getwrd(src, iter); - dest->bitmap[idx] = getwrd(src, iter); - /* iter == size * BMP_BIT - head - 1 - BMP_BIT * (size - 1 - datidx) - * == BMP_BIT * (size - size + 1 + datidx) - head - 1 - * == BMP_BIT * (1 + head / BMP_BIT) - head - 1 - * == BMP_BIT + head - head % BMP_BIT - head - 1 - * == BMP_BIT - head % BMP_BIT - 1 - * >= 0 - */ - } else if(head <= start) { - /* shifting left or copying */ - /* index of the source bit ending up in the LSB of bitmap[idx] */ - iter = start - head + BMP_BIT - 1UL; - for(idx = datidx; idx < size; iter += BMP_BIT, ++idx) - dest->bitmap[idx] = getwrd(src, iter); - } - - /* clear head */ - for(idx = 0UL; idx < datidx; ++idx) - dest->bitmap[idx] = BMP_C(0); - if(size) - dest->bitmap[datidx] &= ~BMP_C(0) >> LOFS(head); - - /* clear tail */ - if(LOFS(length)) - dest->bitmap[size - 1UL] &= ~(~BMP_C(0) >> LOFS(length)); - for(idx = size; idx < fullsize; ++idx) - dest->bitmap[idx] = BMP_C(0); - - /* call praloc to shrink poly if required */ - if(dest->length > fulllength) - praloc(dest, fulllength); -} - -void -ppaste(poly_t *dest, const poly_t src, unsigned long skip, unsigned long seek, unsigned long end, unsigned long fulllength) { - /* pastes terms of src, starting from skip, to positions seek to end-1 of dest - * then sets length of dest to fulllength (>= end) - * to paste n terms of src, give end = seek + n - * to truncate dest at end of paste, set fulllength = end - * to avoid truncating, set fulllength = plen(*dest) - * dest may point to src, in which case the poly is edited in place. - * src must be CLEAN in the case that the end is overrun. - * On exit, dest is CLEAN. - */ - bmp_t mask; - unsigned long seekidx, endidx, iter; - int seekofs; - if(end < seek) end = seek; - if(fulllength < end) fulllength = end; - - /* expand dest if necessary. don't shrink as dest may be src */ - if(fulllength > dest->length) - praloc(dest, fulllength); - seekidx = IDX(seek); - endidx = IDX(end); - seekofs = OFS(seek); - /* index of the source bit ending up in the LSB of the first modified word */ - iter = skip + seekofs; - if(seekidx == endidx) { - /* paste affects one word (traps end = seek case) */ - mask = ((BMP_C(1) << seekofs) - (BMP_C(1) << OFS(end))) << 1; - dest->bitmap[seekidx] = (dest->bitmap[seekidx] & ~mask) | (getwrd(src, iter) & mask); - } else if(seek > skip) { - /* shifting right */ - /* index of the source bit ending up in the LSB of the last modified word */ - iter += (endidx - seekidx) * BMP_BIT; - mask = ~BMP_C(0) >> LOFS(end); - dest->bitmap[endidx] = (dest->bitmap[endidx] & mask) | (getwrd(src, iter) & ~mask); - for(iter -= BMP_BIT, --endidx; endidx > seekidx; iter -= BMP_BIT, --endidx) - dest->bitmap[endidx] = getwrd(src, iter); - mask = ~BMP_C(0) >> LOFS(seek); - dest->bitmap[endidx] = (dest->bitmap[endidx] & ~mask) | (getwrd(src, iter) & mask); - /* iter == skip + seekofs + (endidx - seekidx) * BMP_BIT - BMP_BIT * (endidx - seekidx) - * == skip + seekofs + BMP_BIT * (endidx - seekidx - endidx + seekidx) - * == skip + seekofs - * >= 0 - */ - } else { - /* shifting left or copying */ - mask = ~BMP_C(0) >> LOFS(seek); - dest->bitmap[seekidx] = (dest->bitmap[seekidx] & ~mask) | (getwrd(src, iter) & mask); - for(iter += BMP_BIT, ++seekidx; seekidx < endidx; iter += BMP_BIT, ++seekidx) - dest->bitmap[seekidx] = getwrd(src, iter); - mask = ~BMP_C(0) >> LOFS(end); - dest->bitmap[seekidx] = (dest->bitmap[seekidx] & mask) | (getwrd(src, iter) & ~mask); - } - /* shrink poly if required */ - if(dest->length > fulllength) - praloc(dest, fulllength); -} - -void -pdiff(poly_t *dest, const poly_t src, unsigned long ofs) { - /* Subtract src from dest (modulo 2) at offset ofs. - * In modulo 2 arithmetic, subtraction is equivalent to addition - * We include an alias for those who wish to retain the distinction - * src and dest must be CLEAN. - */ - psum(dest, src, ofs); -} - -void -psum(poly_t *dest, const poly_t src, unsigned long ofs) { - /* Adds src to dest (modulo 2) at offset ofs. - * When ofs == dest->length, catenates src on to dest. - * src and dest must be CLEAN. - */ - unsigned long fulllength, idx, iter, end; - - fulllength = ofs + src.length; - if(fulllength > dest->length) - praloc(dest, fulllength); - /* array index of first word in dest to be modified */ - idx = IDX(ofs); - /* index of bit in src to be added to LSB of dest->bitmap[idx] */ - iter = OFS(ofs); - /* stop value for iter */ - end = BMP_BIT - 1UL + src.length; - for(; iter < end; iter += BMP_BIT, ++idx) - dest->bitmap[idx] ^= getwrd(src, iter); -} - -void -prev(poly_t *poly) { - /* Reverse or reciprocate a polynomial. - * On exit, poly is CLEAN. - */ - unsigned long leftidx = 0UL, rightidx = SIZE(poly->length); - unsigned long ofs = LOFS(BMP_BIT - LOFS(poly->length)); - unsigned long fulllength = poly->length + ofs; - bmp_t accu; - - if(ofs) { - /* removable optimisation */ - if(poly->length < (unsigned long) BMP_BIT) { - *poly->bitmap = rev(*poly->bitmap >> ofs, (int) poly->length) << ofs; - return; - } - } - - /* claim remaining bits of last word (as we use public function pshift()) */ - poly->length = fulllength; - - /* reverse and swap words in the array, leaving it right-justified */ - while(leftidx < rightidx) { - /* rightidx > 0 */ - accu = rev(poly->bitmap[--rightidx], BMP_BIT); - poly->bitmap[rightidx] = rev(poly->bitmap[leftidx], BMP_BIT); - poly->bitmap[leftidx++] = accu; - } - /* shift polynomial to left edge if required */ - if(ofs) - pshift(poly, *poly, 0UL, ofs, fulllength, 0UL); -} - -void -prevch(poly_t *poly, int bperhx) { - /* Reverse each group of bperhx bits in a polynomial. - * Does not clean poly. - */ - unsigned long iter = 0, idx, ofs; - bmp_t mask, accu; - - if(bperhx < 2 || bperhx > BMP_BIT) - return; - if(poly->length % bperhx) - praloc(poly, bperhx - (poly->length % bperhx) + poly->length); - mask = ~BMP_C(0) >> (BMP_BIT - bperhx); - for(iter = (unsigned long) (bperhx - 1); iter < poly->length; iter += bperhx) { - accu = getwrd(*poly, iter) & mask; - accu ^= rev(accu, bperhx); - idx = IDX(iter); - ofs = OFS(iter); - poly->bitmap[idx] ^= accu << ofs; - if(ofs + bperhx > (unsigned int) BMP_BIT) - /* (BMP_BIT - 1UL - (iter) % BMP_BIT) + bperhx > BMP_BIT - * (-1UL - (iter) % BMP_BIT) + bperhx > 0 - * (- (iter % BMP_BIT)) + bperhx > 1 - * - (iter % BMP_BIT) > 1 - bperhx - * iter % BMP_BIT < bperhx - 1, iter >= bperhx - 1 - * iter >= BMP_BIT - * idx >= 1 - */ - poly->bitmap[idx-1] ^= accu >> (BMP_BIT - ofs); - } -} - -void -prcp(poly_t *poly) { - /* Reciprocate a chopped polynomial. Use prev() on whole - * polynomials. - * On exit, poly is SEMI-NORMALISED. - */ - unsigned long first; - - praloc(poly, RNDUP(poly->length)); - prev(poly); - first = pfirst(*poly); - if(first >= poly->length) { - pfree(poly); - return; - } - pshift(poly, *poly, 0UL, first + 1UL, poly->length, 1UL); - piter(poly); -} - -void -pinv(poly_t *poly) { - /* Invert a polynomial, i.e. add 1 (modulo 2) to the coefficient of each term - * on exit, poly is CLEAN. - */ - unsigned long idx, size = SIZE(poly->length); - - for(idx = 0UL; idxbitmap[idx] = ~poly->bitmap[idx]; - if(LOFS(poly->length)) - poly->bitmap[size - 1UL] &= ~(~BMP_C(0) >> LOFS(poly->length)); -} - -poly_t -pmod(const poly_t dividend, const poly_t divisor) { - /* Divide dividend by normalised divisor and return the remainder - * This function generates a temporary 'chopped' divisor for pcrc() - * If calling repeatedly with a constant divisor, produce a chopped copy - * with pchop() and call pcrc() directly for higher efficiency. - * dividend and divisor must be CLEAN. - */ - - /* perhaps generate an error if divisor is zero */ - poly_t subdivisor = psubs(divisor, 0UL, pfirst(divisor) + 1UL, plast(divisor), 0UL); - poly_t result = pcrc(dividend, subdivisor, pzero, pzero, 0); - pfree(&subdivisor); - return(result); -} - -poly_t -pcrc(const poly_t message, const poly_t divisor, const poly_t init, const poly_t xorout, int flags) { - /* Divide message by divisor and return the remainder. - * init is added to divisor, highest terms aligned, before - * division. - * xorout is added to the remainder, highest terms aligned. - * If P_MULXN is set in flags, message is multiplied by x^n - * (i.e. trailing zeroes equal to the CRC width are appended) - * before adding init and division. Set P_MULXN for most CRC - * calculations. - * All inputs must be CLEAN. - * If all inputs are CLEAN, the returned poly_t will be CLEAN. - */ - unsigned long max = 0UL, iter, ofs, resiter; - bmp_t probe, rem, dvsr, *rptr, *sptr; - const bmp_t *bptr, *eptr; - poly_t result = PZERO; - - if(flags & P_MULXN) - max = message.length; - else if(message.length > divisor.length) - max = message.length - divisor.length; - bptr=message.bitmap; - eptr=message.bitmap+SIZE(message.length); - probe=~(~BMP_C(0) >> 1); - if(divisor.length <= (unsigned long) BMP_BIT - && init.length <= (unsigned long) BMP_BIT) { - rem = init.length ? *init.bitmap : BMP_C(0); - dvsr = divisor.length ? *divisor.bitmap : BMP_C(0); - for(iter = 0UL, ofs = 0UL; iter < max; ++iter, --ofs) { - if(!ofs) { - ofs = BMP_BIT; - rem ^= *bptr++; - } - if(rem & probe) - rem = (rem << 1) ^ dvsr; - else - rem <<= 1; - } - if(bptr < eptr) - /* max < message.length */ - rem ^= *bptr >> OFS(BMP_BIT - 1UL + max); - if(init.length > max && init.length - max > divisor.length) { - palloc(&result, init.length - max); - *result.bitmap = rem; - } else if(divisor.length) { - palloc(&result, divisor.length); - *result.bitmap = rem; - } - } else { - /* allocate maximum size plus one word for shifted divisors and one word containing zero. - * This also ensures that result[1] exists - */ - palloc(&result, (init.length > divisor.length ? init.length : divisor.length) + (unsigned long) (BMP_BIT << 1)); - /*if there is content in init, there will be an extra word in result to clear it */ - psum(&result, init, 0UL); - if(max) - *result.bitmap ^= *bptr++; - for(iter = 0UL, ofs = 0UL; iter < max; ++iter, probe >>= 1) { - if(!probe) { - probe = ~(~BMP_C(0) >> 1); - ofs = 0UL; - sptr = rptr = result.bitmap; - ++sptr; - /* iter < max <= message.length, so bptr is valid - * shift result one word to the left, splicing in a message word - * and clearing the last active word - */ - *rptr++ = *sptr++ ^ *bptr++; - for(resiter = (unsigned long) (BMP_BIT << 1); resiter < result.length; resiter += BMP_BIT) - *rptr++ = *sptr++; - } - ++ofs; - if(*result.bitmap & probe) - psum(&result, divisor, ofs); - } - rptr = result.bitmap; - ++rptr; - while(bptr < eptr) - *rptr++ ^= *bptr++; - /* 0 <= ofs <= BMP_BIT, location of the first bit of the result */ - pshift(&result, result, 0UL, ofs, (init.length > max + divisor.length ? init.length - max - divisor.length : 0UL) + divisor.length + ofs, 0UL); - } - psum(&result, xorout, 0UL); - return(result); -} - -int -piter(poly_t *poly) { - /* Replace poly with the 'next' polynomial of equal length. - * Returns zero if the next polynomial is all zeroes, a nonzero - * value otherwise. - * Does not clean poly. - */ - bmp_t *bptr; - if(!poly->length) return(0); - - bptr = poly->bitmap + IDX(poly->length - 1UL); - *bptr += BMP_C(1) << OFS(poly->length - 1UL); - while(bptr != poly->bitmap && !*bptr) - ++(*--bptr); - return(*bptr != BMP_C(0)); -} - -void -palloc(poly_t *poly, unsigned long length) { - /* Replaces poly with a CLEAN object of the specified length, - * consisting of all zeroes. - * It is safe to call with length = 0, in which case the object - * is freed. - * poly may or may not be WELL-FORMED. - * On exit, poly is CLEAN. - */ - unsigned long size = SIZE(length); - - poly->length = 0UL; - free(poly->bitmap); - poly->bitmap = NULL; - if(!length) return; - if(!size) - size = IDX(length) + 1UL; - poly->bitmap = (bmp_t *) calloc(size, sizeof(bmp_t)); - if(poly->bitmap) { - poly->length = length; - } else - uerror("cannot allocate memory for poly"); -} - -void -pfree(poly_t *poly) { - /* Frees poly's bitmap storage and sets poly equal to the empty - * polynomial (PZERO). - * poly may or may not be WELL-FORMED. - * On exit, poly is CLEAN. - */ - - /* palloc(poly, 0UL); */ - - poly->length = 0UL; - free(poly->bitmap); - poly->bitmap = NULL; -} - -void -praloc(poly_t *poly, unsigned long length) { - /* Trims or extends poly to length at the right edge, appending - * zeroes if necessary. - * On entry, poly may or may not be WELL-FORMED. - * On exit, poly is CLEAN. - */ - unsigned long oldsize, size = SIZE(length); - if(!poly) return; - if(!length) { - poly->length = 0UL; - free(poly->bitmap); - poly->bitmap = NULL; - return; - } - if(!size) - size = IDX(length) + 1UL; - if(!poly->bitmap) - poly->length = 0UL; - oldsize = SIZE(poly->length); - if(oldsize != size) - /* reallocate if array pointer is null or array resized */ - poly->bitmap = (bmp_t *) realloc((void *)poly->bitmap, size * sizeof(bmp_t)); - if(poly->bitmap) { - if(poly->length < length) { - /* poly->length >= 0, length > 0, size > 0. - * poly expanded. clear old last word and all new words - */ - if(LOFS(poly->length)) - poly->bitmap[oldsize - 1UL] &= ~(~BMP_C(0) >> LOFS(poly->length)); - while(oldsize < size) - poly->bitmap[oldsize++] = BMP_C(0); - } else if(LOFS(length)) - /* poly->length >= length > 0. - * poly shrunk. clear new last word - */ - poly->bitmap[size - 1UL] &= ~(~BMP_C(0) >> LOFS(length)); - poly->length = length; - } else - uerror("cannot reallocate memory for poly"); -} - -int -pmpar(const poly_t poly, const poly_t mask) { - /* Return even parity of poly masked with mask. - * Poly and mask must be CLEAN. - */ - bmp_t res = BMP_C(0); - int i = BMP_SUB; - const bmp_t *pptr = poly.bitmap, *mptr = mask.bitmap; - const bmp_t *const pend = poly.bitmap + SIZE(poly.length); - const bmp_t *const mend = mask.bitmap + SIZE(mask.length); - - while(pptr < pend && mptr < mend) - res ^= *pptr++ & *mptr++; - do - res ^= res >> i; - while(i >>= 1); - - return((int) (res & BMP_C(1))); -} - -int -pident(const poly_t a, const poly_t b) { - /* Return nonzero if a and b have the same length - * and point to the same bitmap. - * a and b need not be CLEAN. - */ - return(a.length == b.length && a.bitmap == b.bitmap); -} - -/* Private functions */ - -static bmp_t -getwrd(const poly_t poly, unsigned long iter) { - /* Fetch unaligned word from poly where LSB of result is - * bit iter of the bitmap (counting from zero). If iter exceeds - * the length of poly then zeroes are appended as necessary. - * Factored from ptostr(). - * poly must be CLEAN. - */ - bmp_t accu = BMP_C(0); - unsigned long idx, size; - int ofs; - - idx = IDX(iter); - ofs = OFS(iter); - size = SIZE(poly.length); - - if(idx < size) - accu |= poly.bitmap[idx] >> ofs; - if(idx && idx <= size && ofs > 0) - accu |= poly.bitmap[idx - 1UL] << (BMP_BIT - ofs); - return(accu); -} - -static bmp_t -rev(bmp_t accu, int bits) { - /* Returns the bitmap word argument with the given number of - * least significant bits reversed and the rest cleared. - */ - static const unsigned char revtab[256] = { - 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, - 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, - 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, - 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, - 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, - 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, - 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, - 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, - 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, - 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, - 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, - 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, - 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, - 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, - 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, - 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, - 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, - 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, - 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, - 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, - 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, - 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, - 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, - 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, - 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, - 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, - 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, - 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, - 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, - 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, - 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, - 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff - }; - bmp_t result = BMP_C(0); - while(bits > 8) { - bits -= 8; - result = result << 8 | revtab[accu & 0xff]; - accu >>= 8; - } - result = result << bits | (bmp_t) (revtab[accu & 0xff] >> (8 - bits)); - return(result); -} - -static void -prhex(char **spp, bmp_t bits, int flags, int bperhx) { - /* Appends a hexadecimal string representing the bperhx least - * significant bits of bits to an external string. - * spp points to a character pointer that in turn points to the - * end of a hex string being built. prhex() advances this - * second pointer by the number of characters written. - * The unused MSBs of bits MUST be cleared. - * Set P_UPPER in flags to write A-F in uppercase. - */ - static const char hex[] = "0123456789abcdef0123456789ABCDEF"; - const int upper = (flags & P_UPPER ? 0x10 : 0); - while(bperhx > 0) { - bperhx -= ((bperhx + 3) & 3) + 1; - *(*spp)++ = hex[(bits >> bperhx & BMP_C(0xf)) | upper]; - } -} diff --git a/client/reveng/reveng.c b/client/reveng/reveng.c deleted file mode 100644 index dd50987c..00000000 --- a/client/reveng/reveng.c +++ /dev/null @@ -1,489 +0,0 @@ -/* reveng.c - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng 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. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -/* 2013-09-16: calini(), calout() work on shortest argument - * 2013-06-11: added sequence number to uprog() calls - * 2013-02-08: added polynomial range search - * 2013-01-18: refactored model checking to pshres(); renamed chkres() - * 2012-05-24: efficiently build Init contribution string - * 2012-05-24: removed broken search for crossed-endian algorithms - * 2012-05-23: rewrote engini() after Ewing; removed modini() - * 2011-01-17: fixed ANSI C warnings - * 2011-01-08: fixed calini(), modini() caters for crossed-endian algos - * 2011-01-04: renamed functions, added calini(), factored pshres(); - * rewrote engini() and implemented quick Init search - * 2011-01-01: reveng() initialises terminating entry, addparms() - * initialises all fields - * 2010-12-26: renamed CRC RevEng. right results, rejects polys faster - * 2010-12-24: completed, first tests (unsuccessful) - * 2010-12-21: completed modulate(), partial sketch of reveng() - * 2010-12-19: started reveng - */ - -/* reveng() can in theory be modified to search for polynomials shorter - * than the full width as well, but this imposes a heavy time burden on - * the full width search, which is the primary use case, as well as - * complicating the search range function introduced in version 1.1.0. - * It is more effective to search for each shorter width directly. - */ - -#include - -#define FILE void -#include "reveng.h" - -static poly_t *modpol(const poly_t init, int rflags, int args, const poly_t *argpolys); -static void engini(int *resc, model_t **result, const poly_t divisor, int flags, int args, const poly_t *argpolys); -static void calout(int *resc, model_t **result, const poly_t divisor, const poly_t init, int flags, int args, const poly_t *argpolys); -static void calini(int *resc, model_t **result, const poly_t divisor, int flags, const poly_t xorout, int args, const poly_t *argpolys); -static void chkres(int *resc, model_t **result, const poly_t divisor, const poly_t init, int flags, const poly_t xorout, int args, const poly_t *argpolys); - -static const poly_t pzero = PZERO; - -model_t * -reveng(const model_t *guess, const poly_t qpoly, int rflags, int args, const poly_t *argpolys) { - /* Complete the parameters of a model by calculation or brute search. */ - poly_t *pworks, *wptr, rem, gpoly; - model_t *result = NULL, *rptr; - int resc = 0; - unsigned long spin = 0, seq = 0; - - if(~rflags & R_HAVEP) { - /* The poly is not known. - * Produce a list of differences between the arguments. - */ - pworks = modpol(guess->init, rflags, args, argpolys); - if(!pworks || !plen(*pworks)) { - free(pworks); - goto requit; - } - /* Initialise the guessed poly to the starting value. */ - gpoly = pclone(guess->spoly); - /* Clear the least significant term, to be set in the - * loop. qpoly does not need fixing as it is only - * compared with odd polys. - */ - if(plen(gpoly)) - pshift(&gpoly, gpoly, 0UL, 0UL, plen(gpoly) - 1UL, 1UL); - - while(piter(&gpoly) && (~rflags & R_HAVEQ || pcmp(&gpoly, &qpoly) < 0)) { - /* For each possible poly of this size, try - * dividing all the differences in the list. - */ - if(!(spin++ & R_SPMASK)) { - uprog(gpoly, guess->flags, seq++); - } - for(wptr = pworks; plen(*wptr); ++wptr) { - /* straight divide message by poly, don't multiply by x^n */ - rem = pcrc(*wptr, gpoly, pzero, pzero, 0); - if(ptst(rem)) { - pfree(&rem); - break; - } else - pfree(&rem); - } - /* If gpoly divides all the differences, it is a - * candidate. Search for an Init value for this - * poly or if Init is known, log the result. - */ - if(!plen(*wptr)) { - /* gpoly is a candidate poly */ - if(rflags & R_HAVEI && rflags & R_HAVEX) - chkres(&resc, &result, gpoly, guess->init, guess->flags, guess->xorout, args, argpolys); - else if(rflags & R_HAVEI) - calout(&resc, &result, gpoly, guess->init, guess->flags, args, argpolys); - else if(rflags & R_HAVEX) - calini(&resc, &result, gpoly, guess->flags, guess->xorout, args, argpolys); - else - engini(&resc, &result, gpoly, guess->flags, args, argpolys); - } - if(!piter(&gpoly)) - break; - } - /* Finished with gpoly and the differences list, free them. - */ - pfree(&gpoly); - for(wptr = pworks; plen(*wptr); ++wptr) - pfree(wptr); - free(pworks); - } - else if(rflags & R_HAVEI && rflags & R_HAVEX) - /* All parameters are known! Submit the result if we get here */ - chkres(&resc, &result, guess->spoly, guess->init, guess->flags, guess->xorout, args, argpolys); - else if(rflags & R_HAVEI) - /* Poly and Init are known, calculate XorOut */ - calout(&resc, &result, guess->spoly, guess->init, guess->flags, args, argpolys); - else if(rflags & R_HAVEX) - /* Poly and XorOut are known, calculate Init */ - calini(&resc, &result, guess->spoly, guess->flags, guess->xorout, args, argpolys); - else - /* Poly is known but not Init; search for Init. */ - engini(&resc, &result, guess->spoly, guess->flags, args, argpolys); - -requit: - if(!(result = realloc(result, ++resc * sizeof(model_t)))) - uerror("cannot reallocate result array"); - rptr = result + resc - 1; - rptr->spoly = pzero; - rptr->init = pzero; - rptr->flags = 0; - rptr->xorout = pzero; - rptr->check = pzero; - rptr->name = NULL; - - return(result); -} - -static poly_t * -modpol(const poly_t init, int rflags, int args, const poly_t *argpolys) { - /* Produce, in ascending length order, a list of differences - * between the arguments in the list by summing pairs of arguments. - * If R_HAVEI is not set in rflags, only pairs of equal length are - * summed. - * Otherwise, sums of right-aligned pairs are also returned, with - * the supplied init poly added to the leftmost terms of each - * poly of the pair. - */ - poly_t work, swap, *result, *rptr, *iptr; - const poly_t *aptr, *bptr, *eptr = argpolys + args; - unsigned long alen, blen; - - if(args < 2) return(NULL); - - if(!(result = malloc(((((args - 1) * args) >> 1) + 1) * sizeof(poly_t)))) - uerror("cannot allocate memory for codeword table"); - - rptr = result; - - for(aptr = argpolys; aptr < eptr; ++aptr) { - alen = plen(*aptr); - for(bptr = aptr + 1; bptr < eptr; ++bptr) { - blen = plen(*bptr); - if(alen == blen) { - work = pclone(*aptr); - psum(&work, *bptr, 0UL); - } else if(rflags & R_HAVEI && alen < blen) { - work = pclone(*bptr); - psum(&work, *aptr, blen - alen); - psum(&work, init, 0UL); - psum(&work, init, blen - alen); - } else if(rflags & R_HAVEI /* && alen > blen */) { - work = pclone(*aptr); - psum(&work, *bptr, alen - blen); - psum(&work, init, 0UL); - psum(&work, init, alen - blen); - } else - work = pzero; - - if(plen(work)) - pnorm(&work); - if((blen = plen(work))) { - /* insert work into result[] in ascending order of length */ - for(iptr = result; iptr < rptr; ++iptr) { - if(plen(work) < plen(*iptr)) { - swap = *iptr; - *iptr = work; - work = swap; - } - else if(plen(*iptr) == blen && !pcmp(&work, iptr)) { - pfree(&work); - work = *--rptr; - break; - } - } - *rptr++ = work; - } - } - } - *rptr = pzero; - return(result); -} - -static void -engini(int *resc, model_t **result, const poly_t divisor, int flags, int args, const poly_t *argpolys) { - /* Search for init values implied by the arguments. - * Method from: Ewing, Gregory C. (March 2010). - * "Reverse-Engineering a CRC Algorithm". Christchurch: - * University of Canterbury. - * - */ - poly_t apoly = PZERO, bpoly, pone = PZERO, *mat, *jptr; - const poly_t *aptr, *bptr, *iptr; - unsigned long alen, blen, dlen, ilen, i, j; - int cy; - - dlen = plen(divisor); - - /* Allocate the CRC matrix */ - if(!(mat = (poly_t *) malloc((dlen << 1) * sizeof(poly_t)))) - uerror("cannot allocate memory for CRC matrix"); - - /* Find arguments of the two shortest lengths */ - alen = blen = plen(*(aptr = bptr = iptr = argpolys)); - for(++iptr; iptr < argpolys + args; ++iptr) { - ilen = plen(*iptr); - if(ilen < alen) { - bptr = aptr; blen = alen; - aptr = iptr; alen = ilen; - } else if(ilen > alen && (aptr == bptr || ilen < blen)) { - bptr = iptr; blen = ilen; - } - } - if(aptr == bptr) { - /* if no arguments are suitable, calculate Init with an - * assumed XorOut of 0. Create a padded XorOut - */ - palloc(&apoly, dlen); - calini(resc, result, divisor, flags, apoly, args, argpolys); - pfree(&apoly); - free(mat); - return; - } - - /* Find the potential contribution of the bottom bit of Init */ - palloc(&pone, 1UL); - piter(&pone); - if(blen < (dlen << 1)) { - palloc(&apoly, dlen); /* >= 1 */ - psum(&apoly, pone, (dlen << 1) - 1UL - blen); /* >= 0 */ - psum(&apoly, pone, (dlen << 1) - 1UL - alen); /* >= 1 */ - } else { - palloc(&apoly, blen - dlen + 1UL); /* > dlen */ - psum(&apoly, pone, 0UL); - psum(&apoly, pone, blen - alen); /* >= 1 */ - } - if(plen(apoly) > dlen) { - mat[dlen] = pcrc(apoly, divisor, pzero, pzero, 0); - pfree(&apoly); - } else { - mat[dlen] = apoly; - } - - /* Find the actual contribution of Init */ - apoly = pcrc(*aptr, divisor, pzero, pzero, 0); - bpoly = pcrc(*bptr, divisor, pzero, apoly, 0); - - /* Populate the matrix */ - palloc(&apoly, 1UL); - for(jptr=mat; jptr j */ - j = pfirst(apoly); - } - if(j < dlen) - mat[j] = apoly; /* pident(mat[j], pzero) || pfirst(mat[j]) == j */ - else - pfree(&apoly); - } - palloc(&bpoly, dlen + 1UL); - psum(&bpoly, pone, dlen); - - /* Iterate through all solutions */ - do { - /* Solve the matrix by Gaussian elimination. - * The parity of the result, masked by each row, should be even. - */ - cy = 1; - apoly = pclone(bpoly); - jptr = mat + dlen; - for(i=0UL; ispoly = pclone(divisor); - rptr->init = pclone(init); - rptr->flags = flags; - rptr->xorout = pclone(xorout); - rptr->name = NULL; - - /* compute check value for this model */ - mcheck(rptr); - - /* callback to notify new model */ - ufound(rptr); -} diff --git a/client/reveng/reveng.h b/client/reveng/reveng.h deleted file mode 100644 index 48dcb31c..00000000 --- a/client/reveng/reveng.h +++ /dev/null @@ -1,214 +0,0 @@ -/* reveng.h - * Greg Cook, 9/Apr/2015 - */ - -/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder - * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook - * - * This file is part of CRC RevEng. - * - * CRC RevEng 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. - * - * CRC RevEng is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CRC RevEng. If not, see . - */ - -#ifndef REVENG_H -#define REVENG_H 1 - -/* Configuration options */ - -#include "config.h" - -#ifndef BMP_T -# error config.h: BMP_T must be defined as unsigned long or a longer unsigned type -#endif - -#ifndef BMP_C -# error config.h: BMP_C() must define a BMP_T constant -#endif - -#if !defined PRESETS && !defined BMPMACRO -# undef BMP_BIT -# undef BMP_SUB -#endif - -#undef BMP_POF2 - -#ifdef BMP_BIT -# ifndef BMP_SUB -# error config.h: BMP_SUB must be defined as the highest power of two that is strictly less than BMP_BIT -# elif BMP_BIT < 32 -# error config.h: BMP_BIT must be at least 32 -# elif BMP_SUB < 16 -# error config.h: BMP_SUB must be at least 16 -# elif (BMP_SUB >= BMP_BIT || BMP_SUB << 1 < BMP_BIT || BMP_SUB & (BMP_SUB - 1)) -# error config.h: BMP_SUB must be defined as the highest power of two that is strictly less than BMP_BIT -# else /* BMP_SUB */ -# define SETBMP() -# endif /* BMP_SUB */ -# if BMP_BIT == 32 -# define BMP_POF2 5 -# elif BMP_BIT == 64 -# define BMP_POF2 6 -# elif BMP_BIT == 128 -# define BMP_POF2 7 -# elif BMP_BIT == 256 -# define BMP_POF2 8 -# elif BMP_BIT == 512 -# define BMP_POF2 9 -# elif BMP_BIT == 1024 -# define BMP_POF2 10 -# elif BMP_BIT == 2048 -# define BMP_POF2 11 -# elif BMP_BIT == 4096 -# define BMP_POF2 12 -# elif BMP_BIT == 8192 -# define BMP_POF2 13 -# elif BMP_BIT == 16384 -# define BMP_POF2 14 -# elif BMP_BIT == 32768 -# define BMP_POF2 15 -# elif BMP_BIT == 65536 -# define BMP_POF2 16 -/* may extend list as required */ -# elif (BMP_BIT & (BMP_BIT - 1)) == 0 -# define BMP_POF2 1 -# endif -#else /* BMP_BIT */ -# define BMP_BIT bmpbit -# define BMP_SUB bmpsub -# define SETBMP() setbmp() -#endif /* BMP_BIT */ - -/* Global definitions */ - -/* CRC RevEng version string */ -#define VERSION "1.3.0" - -/* bmpbit.c */ -typedef BMP_T bmp_t; - -extern int bmpbit, bmpsub; -extern void setbmp(void); - -/* poly.c */ -#define P_REFIN 1 -#define P_REFOUT 2 -#define P_MULXN 4 -#define P_RTJUST 8 -#define P_UPPER 16 -#define P_SPACE 32 -#define P_LTLBYT 64 -#define P_DIRECT 128 - -/* default flags */ -#define P_BE (P_RTJUST | P_MULXN) -#define P_LE (P_REFIN | P_REFOUT | P_MULXN) -#define P_BELE (P_REFOUT | P_MULXN) -#define P_LEBE (P_REFIN | P_RTJUST | P_MULXN) - -/* A poly_t constant representing the polynomial 0. */ -#define PZERO {0UL, (bmp_t *) 0} - -typedef struct { - unsigned long length; /* number of significant bits */ - bmp_t *bitmap; /* bitmap, MSB first, */ - /* left-justified in each word */ -} poly_t; - -extern poly_t filtop(FILE *input, unsigned long length, int flags, int bperhx); -extern poly_t strtop(const char *string, int flags, int bperhx); -extern char *ptostr(const poly_t poly, int flags, int bperhx); -extern char *pxsubs(const poly_t poly, int flags, int bperhx, unsigned long start, unsigned long end); -extern poly_t pclone(const poly_t poly); -extern void pcpy(poly_t *dest, const poly_t src); -extern void pcanon(poly_t *poly); -extern void pnorm(poly_t *poly); -extern void psnorm(poly_t *poly); -extern void pchop(poly_t *poly); -extern void pkchop(poly_t *poly); -extern unsigned long plen(const poly_t poly); -extern int pcmp(const poly_t *a, const poly_t *b); -extern int psncmp(const poly_t *a, const poly_t *b); -extern int ptst(const poly_t poly); -extern unsigned long pfirst(const poly_t poly); -extern unsigned long plast(const poly_t poly); -extern poly_t psubs(const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail); -extern void pright(poly_t *poly, unsigned long length); -extern void pshift(poly_t *dest, const poly_t src, unsigned long head, unsigned long start, unsigned long end, unsigned long tail); -extern void ppaste(poly_t *dest, const poly_t src, unsigned long skip, unsigned long seek, unsigned long end, unsigned long fulllength); -extern void pdiff(poly_t *dest, const poly_t src, unsigned long ofs); -extern void psum(poly_t *dest, const poly_t src, unsigned long ofs); -extern void prev(poly_t *poly); -extern void prevch(poly_t *poly, int bperhx); -extern void prcp(poly_t *poly); -extern void pinv(poly_t *poly); -extern poly_t pmod(const poly_t dividend, const poly_t divisor); -extern poly_t pcrc(const poly_t message, const poly_t divisor, const poly_t init, const poly_t xorout, int flags); -extern int piter(poly_t *poly); -extern void palloc(poly_t *poly, unsigned long length); -extern void pfree(poly_t *poly); -extern void praloc(poly_t *poly, unsigned long length); -extern int pmpar(const poly_t poly, const poly_t mask); -extern int pident(const poly_t a, const poly_t b); - -/* model.c */ -#define M_OVERWR 256 - -typedef struct { - poly_t spoly; /* polynomial with highest-order term removed. length determines CRC width */ - poly_t init; /* initial register value. length == poly.length */ - int flags; /* P_REFIN and P_REFOUT indicate reflected input/output */ - poly_t xorout; /* final register XOR mask. length == poly.length */ - poly_t check; /* optional check value, the CRC of the UTF-8 string "123456789" */ - const char *name; /* optional canonical name of the model */ -} model_t; - -extern void mcpy(model_t *dest, const model_t *src); -extern void mfree(model_t *model); -extern int mcmp(const model_t *a, const model_t *b); -extern int mbynam(model_t *dest, const char *key); -extern void mbynum(model_t *dest, int num); -extern int mcount(void); -extern char *mnames(void); -extern char *mtostr(const model_t *model); -extern void mmatch(model_t *model, int flags); -extern void mcanon(model_t *model); -extern void mcheck(model_t *model); -extern void mrev(model_t *model); -extern void mnovel(model_t *model); - -/* reveng.c */ -#define R_HAVEP 512 -#define R_HAVEI 1024 -#define R_HAVERI 2048 -#define R_HAVERO 4096 -#define R_HAVEX 8192 -#define R_HAVEQ 16384 - -#define R_SPMASK 0x7FFFFFFUL - -extern model_t *reveng(const model_t *guess, const poly_t qpoly, int rflags, int args, const poly_t *argpolys); - -/* cli.c */ -#define C_INFILE 1 -#define C_FORCE 2 -#define C_RESULT 4 - -#define BUFFER 32768 - -extern int reveng_main(int argc, char *argv[]); -extern void ufound(const model_t *model); -extern void uerror(const char *msg); -extern void uprog(const poly_t gpoly, int flags, unsigned long seq); - -#endif /* REVENG_H */ diff --git a/client/scripting.c b/client/scripting.c index 13cb8cdf..232da889 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -15,6 +15,7 @@ #include #include #include "proxmark3.h" +#include "comms.h" #include "usb_cmd.h" #include "cmdmain.h" #include "util.h" @@ -23,9 +24,9 @@ #include "iso14443crc.h" #include "../common/crc16.h" #include "../common/crc64.h" -#include "../common/sha1.h" -#include "polarssl/aes.h" -#include "cmdcrc.h" +#include "../common/polarssl/sha1.h" +#include "../common/polarssl/aes.h" + /** * The following params expected: * UsbCommand c @@ -220,7 +221,7 @@ static int l_iso14443b_crc(lua_State *L) unsigned char buf[USB_CMD_DATA_SIZE]; size_t len = 0; const char *data = luaL_checklstring(L, 1, &len); - if (USB_CMD_DATA_SIZE < len) + if (len > USB_CMD_DATA_SIZE-2) len = USB_CMD_DATA_SIZE-2; for (int i = 0; i < len; i += 2) { @@ -391,62 +392,6 @@ static int l_sha1(lua_State *L) return 1; } -static int l_reveng_models(lua_State *L){ - - char *models[80]; - int count = 0; - int in_width = luaL_checkinteger(L, 1); - - if( in_width > 89 ) return returnToLuaWithError(L,"Width cannot exceed 89, got %d", in_width); - - uint8_t width[80]; - width[0] = (uint8_t)in_width; - int ans = GetModels(models, &count, width); - if (!ans) return 0; - - lua_newtable(L); - - for (int i = 0; i < count; i++){ - lua_pushstring(L, (const char*)models[i]); - lua_rawseti(L,-2,i+1); - free(models[i]); - } - - return 1; -} - -//Called with 4 parameters. -// inModel ,string containing the crc model name: 'CRC-8' -// inHexStr ,string containing the hex representation of the data that will be used for CRC calculations. -// reverse ,int 0/1 (bool) if 1, calculate the reverse CRC -// endian ,char, 'B','b','L','l','t','r' describing if Big-Endian or Little-Endian should be used in different combinations. -// -// outputs: string with hex representation of the CRC result -static int l_reveng_RunModel(lua_State *L){ - //-c || -v - //inModel = valid model name string - CRC-8 - //inHexStr = input hex string to calculate crc on - //reverse = reverse calc option if true - //endian = {0 = calc default endian input and output, b = big endian input and output, B = big endian output, r = right justified - // l = little endian input and output, L = little endian output only, t = left justified} - //result = calculated crc hex string - char result[50]; - - const char *inModel = luaL_checkstring(L, 1); - const char *inHexStr = luaL_checkstring(L, 2); - bool reverse = lua_toboolean(L, 3); - const char endian = luaL_checkstring(L, 4)[0]; - - //PrintAndLog("mod: %s, hex: %s, rev %d", inModel, inHexStr, reverse); - //int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *result) - int ans = RunModel( (char *)inModel, (char *)inHexStr, reverse, endian, result); - if (!ans) - return returnToLuaWithError(L,"Reveng failed"); - - lua_pushstring(L, (const char*)result); - return 1; -} - /** * @brief Sets the lua path to include "./lualibs/?.lua", in order for a script to be * able to do "require('foobar')" if foobar.lua is within lualibs folder. @@ -493,8 +438,6 @@ int set_pm3_libraries(lua_State *L) {"crc16", l_crc16}, {"crc64", l_crc64}, {"sha1", l_sha1}, - {"reveng_models", l_reveng_models}, - {"reveng_runmodel", l_reveng_RunModel}, {NULL, NULL} }; diff --git a/client/scripts/14araw.lua b/client/scripts/14araw.lua index 0ce98656..e2530929 100644 --- a/client/scripts/14araw.lua +++ b/client/scripts/14araw.lua @@ -17,6 +17,8 @@ Arguments: -p stay connected - dont inactivate the field -x Data to send (NO SPACES!) -d Debug flag + -t Topaz mode + -3 Skip ISO14443-4 select Examples : @@ -70,6 +72,7 @@ function help() print(example) end + --- -- The main entry point function main(args) @@ -83,15 +86,19 @@ function main(args) local stayconnected = false local payload = nil local doconnect = true + local topaz_mode = false + local no_rats = false -- Read the parameters - for o, a in getopt.getopt(args, 'corcpx:') do + for o, a in getopt.getopt(args, 'orcpx:dt3') do if o == "o" then doconnect = false end if o == "r" then ignore_response = true end if o == "c" then appendcrc = true end if o == "p" then stayconnected = true end if o == "x" then payload = a end if o == "d" then DEBUG = true end + if o == "t" then topaz_mode = true end + if o == "3" then no_rats = true end end -- First of all, connect @@ -99,7 +106,7 @@ function main(args) dbg("doconnect") -- We reuse the connect functionality from a -- common library - info, err = lib14a.read1443a(true) + info, err = lib14a.read14443a(true, no_rats) if err then return oops(err) end print(("Connected to card, uid = %s"):format(info.uid)) @@ -107,7 +114,7 @@ function main(args) -- The actual raw payload, if any if payload then - res,err = sendRaw(payload,{ignore_response = ignore_response}) + res,err = sendRaw(payload,{ignore_response = ignore_response, topaz_mode = topaz_mode}) if err then return oops(err) end if not ignoreresponse then @@ -137,11 +144,11 @@ function showdata(usbpacket) end - function sendRaw(rawdata, options) print(">> ", rawdata) local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + lib14a.ISO14A_COMMAND.ISO14A_RAW + if options.topaz_mode == true then flags = flags + lib14a.ISO14A_COMMAND.ISO14A_TOPAZMODE end local command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, arg1 = flags, -- Send raw diff --git a/client/scripts/brutesim.lua b/client/scripts/brutesim.lua new file mode 100644 index 00000000..326966c6 --- /dev/null +++ b/client/scripts/brutesim.lua @@ -0,0 +1,229 @@ +desc = [[ + + .-----------------------------------------------------------------. + / .-. .-. \ +| / \ BruteSim / \ | +| |\_. | (bruteforce simulation for multiple tags) | /| | +|\| | /| by |\ | |/| +| `---' | Kenzy Carey | `---' | +| | | | +| |-----------------------------------------------------| | +\ | | / + \ / \ / + `---' `---' +]] +author = [[ Kenzy Carey ]] +usage = [[ + +USAGE: +script run brutesim -r rfid_tag -f facility_code -b base_card_number -c count -t timeout -d direction +option argument description +------ -------- ----------- +-r *see below RFID Tag: the RFID tag to emulate +-f 0-999 Facility Code: The facility code (dfx: country id, 14a: type) +-b 0-65535 Base Card Number: base card number to start from +-c 1-65536 Count: number of cards to try +-t .0-99999, pause Timeout: timeout between cards (use the word 'pause' to wait for user input) +-d up, down Direction: direction to move through card numbers +-h Show this + +*SUPPORTED TAGS: pyramid, awid, fdx, jablotron, noralsy, presco, visa2000, 14a, hid + +EXAMPLE: +script run brutesim -r pyramid -f 10 -b 1000 -c 10 -t 1 -d down +(the above example would bruteforce pyramid tags, starting at 10:1000, ending at 10:991, and waiting 1 second between each card) +]] + +-- I wrote this as i was doing a PACS audit. This is far from complete, but is easily expandable. +-- The idea was based on proxbrute, but i needed more options, and support for different readers. +-- I dont know LUA, so I used Brian Redbeards lf_bulk_program.lua script as a starting point, sorry if its kludgy. + +getopt = require('getopt') -- Used to get get command line arguments +bit32 = require('bit32') -- Used to convert FC/CN to hex + +local function isempty(s) -- Check if a string is empty + return s == nil or s == '' +end + +local function main(args) + + print("") -- Print a blank line to make things look cleaner + + for o, a in getopt.getopt(args, 'r:f:b:c:t:d:h') do -- Populate command like arguments + if o == 'r' then rfidtag = a end + if o == 'f' then facility = a end + if o == 'b' then baseid = a end + if o == 'c' then count = a end + if o == 't' then timeout = a end + if o == 'd' then direction = a end + if o == 'h' then return print(usage) end + end + + if isempty(rfidtag) then -- Check to see if -r argument was passed + print("You must supply the flag -r (rfid tag)") + print(usage) + return + end + -- Check what RFID Tag we are using + if rfidtag == 'pyramid' then -- For eaach RFID Tag: + consolecommand = 'lf pyramid sim' -- Set the console command + rfidtagname = 'Farpointe/Pyramid' -- Set the display name + facilityrequired = 1 -- Set if FC is required + elseif rfidtag == 'awid' then + consolecommand = 'lf awid sim' + rfidtagname = 'AWID' + facilityrequired = 1 + elseif rfidtag == 'fdx' then -- I'm not sure why you would need to bruteforce this ¯\_(ツ)_/¯ + consolecommand = 'lf fdx sim' + rfidtagname = 'FDX-B' + facilityrequired = 1 + elseif rfidtag == 'jablotron' then + consolecommand = 'lf jablotron sim' + rfidtagname = 'Jablotron' + facilityrequired = 0 + elseif rfidtag == 'noralsy' then + consolecommand = 'lf noralsy sim' + rfidtagname = 'Noralsy' + facilityrequired = 0 + elseif rfidtag == 'presco' then + consolecommand = 'lf presco sim d' + rfidtagname = 'Presco' + facilityrequired = 0 + elseif rfidtag == 'visa2000' then + consolecommand = 'lf visa2000 sim' + rfidtagname = 'Visa2000' + facilityrequired = 0 + elseif rfidtag == '14a' then + consolecommand = 'hf 14a sim' + if facility == "1" then rfidtagname = 'MIFARE Classic' -- Here we use the -f option to read the 14a type instead of the facility code + elseif facility == "2" then rfidtagname = 'MIFARE Ultralight' + elseif facility == "3" then rfidtagname = 'MIFARE Desfire' + elseif facility == "4" then rfidtagname = 'ISO/IEC 14443-4' + elseif facility == "5" then rfidtagname = 'MIFARE Tnp3xxx' + else + print("Invalid 14a type (-f) supplied. Must be 1-5") + print(usage) + return + end + facilityrequired = 0 -- Disable the FC required check, as we used it for type instead of FC + elseif rfidtag == 'hid' then + consolecommand = 'lf hid sim' + rfidtagname = 'HID' + facilityrequired = 1 + else -- Display error and exit out if bad RFID tag was supplied + print("Invalid rfid tag (-r) supplied") + print(usage) + return + end + + if isempty(baseid) then -- Display error and exit out if no starting id is set + print("You must supply the flag -b (base id)") + print(usage) + return + end + + if isempty(count) then -- Display error and exit out of no count is set + print("You must supply the flag -c (count)") + print(usage) + return + end + + if facilityrequired == 1 then -- If FC is required + facilitymessage = " - Facility Code: " -- Add FC to status message + if isempty(facility) then -- If FC was left blank, display warning and set FC to 0 + print("Using 0 for the facility code as -f was not supplied") + facility = 0 + end + else -- If FC is not required + facility = "" -- Clear FC + facilitymessage = "" -- Remove FC from status message + end + + if isempty(timeout) then -- If timeout was not supplied, show warning and set timeout to 0 + print("Using 0 for the timeout as -t was not supplied") + timeout = 0 + end + + if isempty(direction) then -- If direction was not supplied, show warning and set direction to down + print("Using down for direction as -d was not supplied") + direction = 'down' + end + + if tonumber(count) < 1 then + print("Count -c must be set to 1 or higher") + return + else + count = count -1 -- Make our count accurate by removing 1, because math + end + + if direction == 'down' then -- If counting down, set up our for loop to count down + endid = baseid - count + fordirection = -1 + elseif direction == 'up' then -- If counting up, set our for loop to count up + endid = baseid + count + fordirection = 1 + else -- If invalid direction was set, show warning and set up our for loop to count down + print("Invalid direction (-d) supplied, using down") + endid = baseid - count + fordirection = -1 + end + + -- The code below was blatantly stolen from Brian Redbeard's lf_bulk_program.lua script + function toBits(num,bits) + bits = bits or math.max(1, select(2, math.frexp(num))) + local t = {} + for b = bits, 1, -1 do + t[b] = math.fmod(num, 2) + num = math.floor((num - t[b]) / 2) + end + return table.concat(t) + end + + local function evenparity(s) + local _, count = string.gsub(s, "1", "") + local p = count % 2 + if (p == 0) then + return(false) + else + return(true) + end + end + + local function isempty(s) + return s == nil or s == '' + end + + local function cardHex(i,f) + fac = bit32.lshift(f,16) + id = bit32.bor(i, fac) + stream=toBits(id,26) + high = evenparity(string.sub(stream,0,12)) and 1 or 0 + low = not evenparity(string.sub(stream,13)) and 1 or 0 + bits = bit32.bor(bit32.lshift(id,1), low) + bits = bit32.bor(bits, bit32.lshift(high,25)) + preamble = bit32.bor(0, bit32.lshift(1,5)) + bits = bit32.bor(bits, bit32.lshift(1,26)) + return ("%04x%08x"):format(preamble,bits) + end + -- End stolen code + + + print("") -- Display status message + print("BruteForcing "..rfidtagname..""..facilitymessage..""..facility.." - CardNumber Start: "..baseid.." - CardNumber End: "..endid.." - TimeOut: "..timeout) + print("") + for cardnum = baseid,endid,fordirection do -- Loop through for each count (-c) + if rfidtag == 'hid' then cardnum = cardHex(cardnum, facility) end -- If rfid tag is set to HID, convert card to HEX using the stolen code above + core.console(consolecommand..' '..facility..' '..cardnum) -- Send command to proxmark + if timeout == 'pause' then -- If timeout is set to pause, wait for user input + print("Press enter to continue ...") + io.read() + else -- Otherwise sleep for timeout duration + os.execute("sleep "..timeout.."") + end + end + core.console('hw ping') -- Ping the proxmark to stop emulation and see if its still responding + +end -- Go bye bye + + +main(args) -- Do the thing diff --git a/client/scripts/didump.lua b/client/scripts/didump.lua index 2386d42d..71bfd99c 100644 --- a/client/scripts/didump.lua +++ b/client/scripts/didump.lua @@ -406,7 +406,7 @@ function main(args) -- GET TAG UID - result, err = lib14a.read1443a(false) + result, err = lib14a.read14443a(false, true) if not result then return oops(err) end diff --git a/client/scripts/e.lua b/client/scripts/e.lua deleted file mode 100644 index a20b8e47..00000000 --- a/client/scripts/e.lua +++ /dev/null @@ -1,72 +0,0 @@ -local getopt = require('getopt') -local utils = require('utils') - -example = "script calculates many different checksums (CRC) over the provided hex input" -author = "Iceman" -desc = -[[ -This script calculates many checksums (CRC) over the provided hex input. - -Arguments: - -b data in hex - -w bitwidth of the CRC family of algorithm. defaults to all known CRC presets. -Examples : - script run e -b 010203040506070809 - script run e -b 010203040506070809 -w 16 -]] - ---- --- A debug printout-function -function dbg(args) - if DEBUG then - print("###", args) - end -end ---- --- This is only meant to be used when errors occur -function oops(err) - print("ERROR: ",err) - return nil,err -end ---- --- Usage help -function help() - print(desc) - print("Example usage") - print(example) -end ---- --- The main entry point -function main(args) - - local data - local width = 0 - - -- Read the parameters - for o, a in getopt.getopt(args, 'hb:w:') do - if o == "h" then return help() end - if o == "b" then data = a end - if o == "w" then width = a end - end - - data = data or '01020304' - - print( string.rep('-',60) ) - print('Bit width of CRC | '..width) - print('Bytes | '..data) - print('') - print( ('%-20s| %-16s| %s'):format('Model','CRC', 'CRC reverse')) - print( string.rep('-',60) ) - local lists = core.reveng_models(width) - for _,i in pairs(lists) do - local a1 = core.reveng_runmodel(i, data, false, '0') - local a2 = core.reveng_runmodel(i, data, true, '0') - local a3 = core.reveng_runmodel(i, data, false, 'b') - local a4 = core.reveng_runmodel(i, data, false, 'B') - local a5 = core.reveng_runmodel(i, data, false, 'l') - local a6 = core.reveng_runmodel(i, data, false, 'L') - print( ('%-20s| %-16s| %-16s| %-16s| %-16s| %-16s| %-16s'):format(i, a1:upper(), a2:upper(),a3:upper(),a4:upper(),a5:upper(),a6:upper() ) ) - end -end - -main(args) \ No newline at end of file diff --git a/client/scripts/formatMifare.lua b/client/scripts/formatMifare.lua index a14c6b78..2861b542 100644 --- a/client/scripts/formatMifare.lua +++ b/client/scripts/formatMifare.lua @@ -71,7 +71,7 @@ end -- -- Read information from a card function GetCardInfo() - result, err = lib14a.read1443a(false) + result, err = lib14a.read14443a(false, true) if not result then print(err) return diff --git a/client/scripts/lf_bulk_program.lua b/client/scripts/lf_bulk_program.lua index 4758c203..2556f8a5 100644 --- a/client/scripts/lf_bulk_program.lua +++ b/client/scripts/lf_bulk_program.lua @@ -9,7 +9,7 @@ bit32 = require('bit32') usage = [[ script run lf_bulk_program.lua -f facility -b base_id_num -c count - e.g: + e.g: script run lf_bulk_program.lua -f 1 -b 1000 -c 10 ]] author = "Brian Redbeard" @@ -32,12 +32,12 @@ function toBits(num,bits) end --[[Likely, I'm an idiot, but I couldn't find any parity functions in Lua - This can also be done with a combination of bitwise operations (in fact, + This can also be done with a combination of bitwise operations (in fact, is the canonically "correct" way to do it, but my brain doesn't just default to this and so counting some ones is good enough for me]]-- local function evenparity(s) local _, count = string.gsub(s, "1", "") - + local p = count % 2 if (p == 0) then return(false) @@ -45,7 +45,7 @@ local function evenparity(s) return(true) end end - + local function isempty(s) return s == nil or s == '' @@ -57,12 +57,13 @@ end local function cardHex(i,f) fac = bit32.lshift(f,16) id = bit32.bor(i, fac) - stream=toBits(id,26) + stream=toBits(id,24) --As the function defaults to even parity and returns a boolean, --perform a 'not' function to get odd parity - high = evenparity(string.sub(stream,0,12)) and 1 or 0 - low = not evenparity(string.sub(stream,13)) and 1 or 0 + high = evenparity(string.sub(stream,1,12)) and 1 or 0 + low = not evenparity(string.sub(stream,13)) and 1 or 0 + bits = bit32.bor(bit32.lshift(id,1), low) bits = bit32.bor(bits, bit32.lshift(high,25)) @@ -71,13 +72,13 @@ local function cardHex(i,f) --to create a higher order and lower order component which we will --then assemble in the return. The math above defines the proper --encoding as per HID/Weigand/etc. These bit flips are due to the - --format length check on bit 38 (cmdlfhid.c:64) and + --format length check on bit 38 (cmdlfhid.c:64) and --bit 31 (cmdlfhid.c:66). preamble = bit32.bor(0, bit32.lshift(1,5)) bits = bit32.bor(bits, bit32.lshift(1,26)) return ("%04x%08x"):format(preamble,bits) - + end local function main(args) @@ -86,22 +87,22 @@ local function main(args) --long arguments, but it seems this library was chosen for BSD style --compatibility for o, a in getopt.getopt(args, 'f:b:c:h') do - if o == 'f' then - if isempty(a) then + if o == 'f' then + if isempty(a) then print("You did not supply a facility code, using 0") facility = 0 - else + else facility = a end - elseif o == 'b' then - if isempty(a) then + elseif o == 'b' then + if isempty(a) then print("You must supply the flag -b (base id)") return else baseid = a end - elseif o == 'c' then - if isempty(a) then + elseif o == 'c' then + if isempty(a) then print("You must supply the flag -c (count)") return else @@ -118,22 +119,22 @@ local function main(args) --works, specifying ":" does not enforce supplying a value, thus we --need to do these checks all over again. - if isempty(baseid) then + if isempty(baseid) then print("You must supply the flag -b (base id)") print(usage) return end - if isempty(count) then + if isempty(count) then print("You must supply the flag -c (count)") print(usage) return end --If the facility ID is non specified, ensure we code it as zero - if isempty(facility) then + if isempty(facility) then print("Using 0 for the facility code as -f was not supplied") - facility = 0 + facility = 0 end --The next baseid + count function presents a logic/UX conflict @@ -144,7 +145,7 @@ local function main(args) endid = baseid + count - for cardnum = baseid,endid do + for cardnum = baseid,endid do local card = cardHex(cardnum, facility) print("Press enter to program card "..cardnum..":"..facility.." (hex: "..card..")") --This would be better with "press any key", but we'll take diff --git a/client/scripts/mfkeys.lua b/client/scripts/mfkeys.lua index c54f6e67..e8abd0b9 100644 --- a/client/scripts/mfkeys.lua +++ b/client/scripts/mfkeys.lua @@ -1,11 +1,11 @@ --[[ This is an example of Lua-scripting within proxmark3. This is a lua-side - implementation of hf mf chk + implementation of hf mf chk This code is licensed to you under the terms of the GNU GPL, version 2 or, at your option, any later version. See the LICENSE.txt file for the text of the license. - + Copyright (C) 2013 m h swende --]] -- Loads the commands-library @@ -14,49 +14,15 @@ local cmds = require('commands') local keys = require('mf_default_keys') -- Ability to read what card is there local reader = require('read14a') +-- Asks the user for input +local utils = require('utils') -local desc = -("This script implements check keys. It utilises a large list of default keys (currently %d keys).\ +local desc = ("This script implements check keys. \ +It utilises a large list of default keys (currently %d keys).\ If you want to add more, just put them inside mf_default_keys.lua. "):format(#keys) local TIMEOUT = 10000 -- 10 seconds - ---[[This may be moved to a separate library at some point]] -local utils = -{ - --- - -- Asks the user for Yes or No - confirm = function(message, ...) - local answer - message = message .. " [y]/[n] ?" - repeat - io.write(message) - io.flush() - answer=io.read() - if answer == 'Y' or answer == "y" then - return true - elseif answer == 'N' or answer == 'n' then - return false - end - until false - end, - --- - -- Asks the user for input - input = function (message , default) - local answer - if default ~= nil then - message = message .. " (default: ".. default.. " )" - end - message = message .." \n > " - io.write(message) - io.flush() - answer=io.read() - if answer == '' then answer = default end - - return answer - end, -} local function checkCommand(command) @@ -83,7 +49,7 @@ end function checkBlock(blockNo, keys, keyType) - -- The command data is only 512 bytes, each key is 6 bytes, meaning that we can send max 85 keys in one go. + -- The command data is only 512 bytes, each key is 6 bytes, meaning that we can send max 85 keys in one go. -- If there's more, we need to split it up local start, remaining= 1, #keys local arg1 = bit32.bor(bit32.lshift(keyType, 8), blockNo) @@ -91,18 +57,18 @@ function checkBlock(blockNo, keys, keyType) while remaining > 0 do local n,data = remaining, nil if remaining > 85 then n = 85 end - local data = table.concat(keys,"",start,n) + local data = table.concat(keys, "", start, start + n - 1) --print("data",data) --print("data len", #data) print(("Testing block %d, keytype %d, with %d keys"):format(blockNo, keyType, n)) - local command = Command:new{cmd = cmds.CMD_MIFARE_CHKKEYS, - arg1 = arg1, - arg2 = 1, - arg3 = n, + local command = Command:new{cmd = cmds.CMD_MIFARE_CHKKEYS, + arg1 = arg1, + arg2 = 1, + arg3 = n, data = data} local status = checkCommand(command) if status then return status, blockNo end - start = start+n+1 + start = start + n remaining = remaining - n end return nil @@ -119,41 +85,43 @@ local function displayresults(results) for sector,_ in pairs(results) do blockNo, keyA, keyB = unpack(_) - print(("| %3d | %3d |%s|%s|"):format(sector, blockNo, keyA, keyB )) + print(("| %3d | %3d |%12s|%12s|"):format(sector, blockNo, keyA, keyB)) end print("|--------------------------------------|") end + -- A little helper to place an item first in the list local function placeFirst(akey, list) - akey = akey:lower() - if list[1] == akey then + akey = akey:lower() + if list[1] == akey then -- Already at pole position return list end local result = {akey} --print(("Putting '%s' first"):format(akey)) for i,v in ipairs(list) do - if v ~= akey then + if v ~= akey then result[#result+1] = v end end return result end + local function dumptofile(results) local sector, blockNo, keyA, keyB,_ - if utils.confirm("Do you wish to save the keys to dumpfile?") then + if utils.confirm("Do you wish to save the keys to dumpfile?") then local destination = utils.input("Select a filename to store to", "dumpkeys.bin") local file = io.open(destination, "w") - if file == nil then + if file == nil then print("Could not write to file ", destination) return end local key_a = "" local key_b = "" - + for sector,_ in pairs(results) do blockNo, keyA, keyB = unpack(_) key_a = key_a .. bin.pack("H",keyA); @@ -166,11 +134,11 @@ local function dumptofile(results) end -local function main( args) +local function main(args) print(desc); - result, err = reader.read1443a() + result, err = reader.read14443a(false, true) if not result then print(err) return @@ -181,11 +149,11 @@ local function main( args) core.clearCommandBuffer() local blockNo local keyType = 0 -- A=0, B=1 - local numSectors = 16 + local numSectors = 16 - if 0x18 == result.sak then --NXP MIFARE Classic 4k | Plus 4k - -- IFARE Classic 4K offers 4096 bytes split into forty sectors, - -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors. + if 0x18 == result.sak then -- NXP MIFARE Classic 4k | Plus 4k + -- IFARE Classic 4K offers 4096 bytes split into forty sectors, + -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors. numSectors = 40 elseif 0x08 == result.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k -- 1K offers 1024 bytes of data storage, split into 16 sector @@ -193,7 +161,7 @@ local function main( args) elseif 0x09 == result.sak then -- NXP MIFARE Mini 0.3k -- MIFARE Classic mini offers 320 bytes split into five sectors. numSectors = 5 - elseif 0x10 == result.sak then-- "NXP MIFARE Plus 2k" + elseif 0x10 == result.sak then -- NXP MIFARE Plus 2k numSectors = 32 else print("I don't know how many sectors there are on this type of card, defaulting to 16") @@ -203,25 +171,25 @@ local function main( args) for sector=1,numSectors,1 do --[[ - The mifare Classic 1k card has 16 sectors of 4 data blocks each. + The mifare Classic 1k card has 16 sectors of 4 data blocks each. The first 32 sectors of a mifare Classic 4k card consists of 4 data blocks and the remaining - 8 sectors consist of 16 data blocks. + 8 sectors consist of 16 data blocks. --]] - local blockNo = sector * 4 -1 - + local blockNo = sector * 4 - 1 + if sector > 32 then - blockNo = 32*4+ (sector-32)*16 -1 + blockNo = 32*4 + (sector-32)*16 - 1 end local keyA = checkBlock(blockNo, keys, 0) - if keyA then keys = placeFirst(keyA, keys) end + if keyA then keys = placeFirst(keyA, keys) end keyA = keyA or "" local keyB = checkBlock(blockNo, keys, 1) - if keyB then keys = placeFirst(keyB, keys) end + if keyB then keys = placeFirst(keyB, keys) end keyB = keyB or "" - result[sector] = {blockNo, keyA, keyB } + result[sector] = {blockNo, keyA, keyB} -- Check if user aborted if core.ukbhit() then @@ -233,5 +201,4 @@ local function main( args) dumptofile(result) end -main( args) - +main(args) diff --git a/client/scripts/mifarePlus.lua b/client/scripts/mifarePlus.lua new file mode 100644 index 00000000..061ff736 --- /dev/null +++ b/client/scripts/mifarePlus.lua @@ -0,0 +1,340 @@ +local cmds = require('commands') +local lib14a = require('read14a') +getopt = require('getopt') -- Used to get command line arguments + +example = "script run mifarePlus" +author = "Dominic Celiano" +desc = +[[ +Purpose: Lua script to communicate with the Mifare Plus EV1, including personalization (setting the keys) and proximity check. Manually edit the file to add to the commands you can send the card. + +Please read the NXP manual before running this script to prevent making irreversible changes. Also note: + - The Mifare Plus must start in SL0 for personalization. Card can then be moved to SL1 or SL3. + - The keys are hardcoded in the script to "00...". Unless you change this, only use this script for testing purposes. + - Make sure you choose your card size correctly (2kB or 4kB). + +Small changes can be to made this script to communicate with the Mifare Plus S, X, or SE. + +Arguments: + -h : this help +]] + +SIXTEEN_BYTES_ZEROS = "00000000000000000000000000000000" + +GETVERS_INIT = "0360" -- Begins the GetVersion command +GETVERS_CONT = "03AF" -- Continues the GetVersion command +POWEROFF = "OFF" +WRITEPERSO = "03A8" +COMMITPERSO = "03AA" +AUTH_FIRST = "0370" +AUTH_CONT = "0372" +AUTH_NONFIRST = "0376" +PREPAREPC = "03F0" +PROXIMITYCHECK = "03F2" +VERIFYPC = "03FD" +READPLAINNOMACUNMACED = "0336" + +--- +-- This is only meant to be used when errors occur +function oops(err) + print("ERROR: ",err) +end + +--- +-- Usage help +function help() + print(desc) + print("Example usage") + print(example) +end + +--- +-- Used to send raw data to the firmware to subsequently forward the data to the card. +function sendRaw(rawdata, crc, power) + print((": %s"):format(rawdata)) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_RAW + if crc then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + end + if power then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + end + + local command = Command:new{cmd = cmds.CMD_READER_ISO_14443a, + arg1 = flags, -- Send raw + arg2 = string.len(rawdata) / 2, -- arg2 contains the length, which is half the length of the ASCII-string rawdata + data = rawdata} + local ignore_response = false + local result, err = lib14a.sendToDevice(command, ignore_response) + if result then + --unpack the first 4 parts of the result as longs, and the last as an extremely long string to later be cut down based on arg1, the number of bytes returned + local count,cmd,arg1,arg2,arg3,data = bin.unpack('LLLLH512',result) + returned_bytes = string.sub(data, 1, arg1 * 2) + if returned_bytes ~= "" then + print((": %s"):format(returned_bytes)) -- need to multiply by 2 because the hex digits are actually two bytes when they are strings + end + return returned_bytes + else + err = "Error sending the card raw data." + oops(err) + end +end + +function writePerso() + -- Used to write any data, including the keys (Key A and Key B), for all the sectors. + -- writePerso() command parameters: + -- 1 byte - 0xA8 - Command Code + -- 2 bytes - Address of the first block or key to be written to (40 blocks are numbered from 0x0000 to 0x00FF) + -- X bytes - The data bytes to be written, starting from the first block. Amount of data sent can be from 16 to 240 bytes in 16 byte increments. This allows + -- up to 15 blocks to be written at once. + -- response from PICC: + -- 0x90 - OK + -- 0x09 - targeted block is invalid for writes, i.e. block 0, which contains manufacturer data + -- 0x0B - command invalid + -- 0x0C - unexpected command length + + + + cardsize = 4 --need to set to 4 for 4k or 2 for 2k + if(cardsize == 4) then + numsectors = 39 + elseif(cardsize == 2) then + numsectors = 31 + else + oops("Invalid card size") + end + + -- Write to the AES sector keys + print("Setting AES Sector keys") + for i=0,numsectors do --for each sector number + local keyA_block = "40" .. string.format("%02x", i * 2) + local keyB_block = "40" .. string.format("%02x", (i * 2) + 1) + --Can also calculate the keys fancily to make them unique, if desired + keyA = SIXTEEN_BYTES_ZEROS + keyB = SIXTEEN_BYTES_ZEROS + writeBlock(keyA_block, keyA) + writeBlock(keyB_block, keyB) + end + print("Finished setting AES Sector keys") + + print("Setting misc keys which haven't been set yet.") + --CardMasterKey + blocknum = "9000" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --CardConfigurationKey + blocknum = "9001" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --L3SwitchKey + blocknum = "9003" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --SL1CardAuthKey + blocknum = "9004" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --L3SectorSwitchKey + blocknum = "9006" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --L1L3MixSectorSwitchKey + blocknum = "9007" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --VC Keys + --VCProximityKey + blocknum = "A001" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --VCSelectENCKey + blocknum = "A080" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --VCSelectMACKey + blocknum = "A081" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --TransactionMACKey1 + blocknum = "C000" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + --TransactionMACConfKey1 + blocknum = "C001" + writeBlock(blocknum, SIXTEEN_BYTES_ZEROS) + print("Finished setting misc keys.") + + print("WritePerso finished! Card is ready to move into new security level.") +end + +function writeBlock(blocknum, data) + -- Method writes 16 bytes of the string sent (data) to the specified block number + -- The block numbers sent to the card need to be in little endian format (i.e. block 0x0001 is sent as 0x1000) + blocknum_little_endian = string.sub(blocknum, 3, 4) .. string.sub(blocknum, 1, 2) + commandString = WRITEPERSO .. blocknum_little_endian .. data --Write 16 bytes (32 hex chars). + response = sendRaw(commandString, true, true) --0x90 is returned upon success + if string.sub(response, 3, 4) ~= "90" then + oops(("error occurred while trying to write to block %s"):format(blocknum)) + end +end + +function authenticateAES() + -- Used to try to authenticate with the AES keys we programmed into the card, to ensure the authentication works correctly. + commandString = AUTH_FIRST + commandString = commandString .. "" +end + +function getVersion() + sendRaw(GETVERS_INIT, true, true) + sendRaw(GETVERS_CONT, true, true) + sendRaw(GETVERS_CONT, true, true) +end + +function commitPerso(SL) + --pass SL as "01" to move to SL1 or "03" to move to SL3. + commandString = COMMITPERSO .. SL + response = sendRaw(commandString, true, true) --0x90 is returned upon success + if string.sub(response, 3, 4) ~= "90" then + oops("error occurred while trying to switch security level") + end +end + +function calculateMAC(MAC_input) + -- Pad the input if it is not a multiple of 16 bytes (32 nibbles). + if(string.len(MAC_input) % 32 ~= 0) then + MAC_input = MAC_input .. "80" + end + while(string.len(MAC_input) % 32 ~= 0) do + MAC_input = MAC_input .. "0" + end + print("Padded MAC Input = " .. MAC_input .. ", length (bytes) = " .. string.len(MAC_input) / 2) + + --The MAC would actually be calculated here, and the output stored in raw_output + raw_output = "00010203040506070001020304050607" -- Dummy filler for now of 16-byte output. To be filled with actual MAC for testing purposes. + + -- The final 8-byte MAC output is a concatenation of every 2nd byte starting from the second MSB. + final_output = "" + j = 3 + for i = 1,8 do + final_output = final_output .. string.sub(RndR, j, j + 1) .. string.sub(RndC, j, j + 1) + j = j + 4 + end + return final_output +end + +function proximityCheck() + --PreparePC-- + commandString = PREPAREPC + response = sendRaw(commandString, true, true) + if(response == "") then + print("ERROR: This card does not support the Proximity Check command.") + return + end + OPT = string.sub(response, 5, 6) + if(tonumber(OPT) == 1) then + pps_present = true + else + pps_present = false + end + pubRespTime = string.sub(response, 7, 10) + if(pps_present == true) then + pps = string.sub(response, 11, 12) + else + pps = nil + end + print("OPT = " .. OPT .. " pubRespTime = " .. pubRespTime .. " pps = " .. pps) + + --PC-- + RndC = "0001020304050607" --Random Challenge + num_rounds = 8 --Needs to be 1, 2, 4, or 8 + part_len = 8 / num_rounds + j = 1 + RndR = "" + for i = 1,num_rounds do + pRndC = "" + for q = 1,(part_len*2) do + pRndC = pRndC .. string.sub(RndC,j,j) + j = j + 1 + end + commandString = PROXIMITYCHECK .. "0" .. tostring(part_len) .. pRndC + pRndR = string.sub(sendRaw(commandString, true, true), 3, 3+part_len) + RndR = RndR .. pRndR + end + print("RndC = " .. RndC .. " RndR = " .. RndR) + + --VerifyPC-- + MAC_input = "FD" .. OPT .. pubRespTime + if(pps_present == true) then + MAC_input = MAC_input .. pps + end + rnum_concat = "" + rnum_concat = RndR .. RndC --temporary (only works for when a single random challenge (8 bytes) is sent) + -- j = 1 + -- for i = 1,8 do + -- rnum_concat = rnum_concat .. string.sub(RndR, j, j + 1) .. string.sub(RndC, j, j + 1) + -- j = j + 2 + -- end + MAC_input = MAC_input .. rnum_concat + print("Concatenation of random numbers = " .. rnum_concat) + print("Final PCD concatenation before input into MAC function = " .. MAC_input) + MAC_tag = calculateMAC(MAC_input) + print("8-byte PCD MAC_tag (placeholder - currently incorrect) = " .. MAC_tag) + commandString = VERIFYPC .. MAC_tag + response = sendRaw(commandString, true, true) + print(response) + PICC_MAC = string.sub(response, 5, 20) + print("8-byte MAC returned by PICC = " .. PICC_MAC) + MAC_input = "90" .. string.sub(MAC_input, 3) + print("Final PICC concatenation before input into MAC function = " .. MAC_input) + MAC_tag = calculateMAC(MAC_input) + print("8-byte PICC MAC_tag (placeholder - currently incorrect) = " .. MAC_tag) + +end + +--- +-- The main entry point +function main(args) + print("") -- Print a blank line to make things look cleaner + for o, a in getopt.getopt(args, 'h') do -- Populate command line arguments + if o == "h" then help() return end + end + + -- Initialize the card using the already-present read14a library + info,err = lib14a.read14443a(true, false) + --Perform RATS and PPS (Protocol and Parameter Selection) check to finish the ISO 14443-4 protocol. + response = sendRaw("e050", true, true) + if(response == "") then + print("No response from RATS.") + end + response = sendRaw("D01100", true, true) + if(response == "") then + print("No response from PPS check.") + end + if err then + oops(err) + sendRaw(POWEROFF, false, false) + return + else + print(("Connected to card with a UID of %s."):format(info.uid)) + end + + + -- Now, the card is initialized and we can do more interesting things. + + --writePerso() + --commitPerso("03") --move to SL3 + --getVersion() + proximityCheck() + + --commandString = VERIFYPC .. "186EFDE8DDC7D30B" + -- MAC = f5180d6e 40fdeae8 e9dd6ac7 bcd3350b + -- response = sendRaw(commandString, true, true) + + -- attempt to read VCProximityKey at block A001 + -- commandString = READPLAINNOMACUNMACED .. "01A0" .. "01" + -- response = sendRaw(commandString, true, true) + + -- authenticate with CardConfigurationKey + -- commandString = AUTH_FIRST .. "0190" .. "00" + -- response = sendRaw(commandString, true, true) + + -- Power off the Proxmark + sendRaw(POWEROFF, false, false) + + + +end + + +main(args) -- Call the main function diff --git a/client/scripts/mifare_autopwn.lua b/client/scripts/mifare_autopwn.lua index e68f7a75..d743f21e 100644 --- a/client/scripts/mifare_autopwn.lua +++ b/client/scripts/mifare_autopwn.lua @@ -56,7 +56,7 @@ end -- @return if unsuccessfull : nil, error function wait_for_mifare() while not core.ukbhit() do - res, err = reader.read1443a() + res, err = reader.read14443a(false, true) if res then return res end -- err means that there was no response from card end diff --git a/client/scripts/tnp3clone.lua b/client/scripts/tnp3clone.lua index e87c338e..9441559b 100644 --- a/client/scripts/tnp3clone.lua +++ b/client/scripts/tnp3clone.lua @@ -122,7 +122,7 @@ local function main(args) -- find tag - result, err = lib14a.read1443a(false) + result, err = lib14a.read14443a(false, true) if not result then return oops(err) end -- load keys diff --git a/client/scripts/tnp3dump.lua b/client/scripts/tnp3dump.lua index 211d146f..3955137e 100644 --- a/client/scripts/tnp3dump.lua +++ b/client/scripts/tnp3dump.lua @@ -127,7 +127,7 @@ local function main(args) local cmdSetDbgOff = "hf mf dbg 0" core.console( cmdSetDbgOff) - result, err = lib14a.read1443a(false) + result, err = lib14a.read14443a(false, true) if not result then return oops(err) end diff --git a/client/ui.c b/client/ui.c index df2c3ce3..50a6ec7d 100644 --- a/client/ui.c +++ b/client/ui.c @@ -9,27 +9,29 @@ // UI utilities //----------------------------------------------------------------------------- -#include +#include +#ifndef EXTERNAL_PRINTANDLOG #include #include -#include +#include #include #include +#endif #include "ui.h" double CursorScaleFactor = 1; int PlotGridX=0, PlotGridY=0, PlotGridXdefault= 64, PlotGridYdefault= 64, CursorCPos= 0, CursorDPos= 0; -int offline; -int flushAfterWrite = 0; //buzzy +bool flushAfterWrite = false; //buzzy int GridOffset = 0; bool GridLocked = false; bool showDemod = true; -extern pthread_mutex_t print_lock; - static char *logfilename = "proxmark3.log"; +#ifndef EXTERNAL_PRINTANDLOG +static pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER; + void PrintAndLog(char *fmt, ...) { char *saved_line; @@ -49,8 +51,11 @@ void PrintAndLog(char *fmt, ...) } } + // If there is an incoming message from the hardware (eg: lf hid read) in + // the background (while the prompt is displayed and accepting user input), + // stash the prompt and bring it back later. #ifdef RL_STATE_READCMD - // We are using GNU readline. + // We are using GNU readline. libedit (OSX) doesn't support this flag. int need_hack = (rl_readline_state & RL_STATE_READCMD) > 0; if (need_hack) { @@ -60,9 +65,6 @@ void PrintAndLog(char *fmt, ...) rl_replace_line("", 0); rl_redisplay(); } -#else - // We are using libedit (OSX), which doesn't support this flag. - int need_hack = 0; #endif va_start(argptr, fmt); @@ -72,6 +74,8 @@ void PrintAndLog(char *fmt, ...) va_end(argptr); printf("\n"); +#ifdef RL_STATE_READCMD + // We are using GNU readline. libedit (OSX) doesn't support this flag. if (need_hack) { rl_restore_prompt(); rl_replace_line(saved_line, 0); @@ -79,6 +83,7 @@ void PrintAndLog(char *fmt, ...) rl_redisplay(); free(saved_line); } +#endif if (logging && logfile) { vfprintf(logfile, fmt, argptr2); @@ -87,16 +92,21 @@ void PrintAndLog(char *fmt, ...) } va_end(argptr2); - if (flushAfterWrite == 1) //buzzy + if (flushAfterWrite) //buzzy { fflush(NULL); } //release lock pthread_mutex_unlock(&print_lock); } - +#endif void SetLogFilename(char *fn) { logfilename = fn; } + +void SetFlushAfterWrite(bool flush_after_write) { + flushAfterWrite = flush_after_write; +} + diff --git a/client/ui.h b/client/ui.h index 4049033d..1273fe9e 100644 --- a/client/ui.h +++ b/client/ui.h @@ -20,11 +20,10 @@ void ShowGraphWindow(void); void RepaintGraphWindow(void); void PrintAndLog(char *fmt, ...); void SetLogFilename(char *fn); +void SetFlushAfterWrite(bool flush_after_write); extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; -extern int offline; -extern int flushAfterWrite; //buzzy extern bool GridLocked; extern bool showDemod; diff --git a/client/util.c b/client/util.c index 38dd3a12..d7c824d9 100644 --- a/client/util.c +++ b/client/util.c @@ -16,7 +16,6 @@ #include #include #include -#include "data.h" #ifdef _WIN32 #include @@ -111,49 +110,54 @@ void FillFileNameByUID(char *fileName, uint8_t * uid, char *ext, int byteCount) sprintf(fnameptr, "%s", ext); } +void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len, + const size_t min_str_len, const size_t spaces_between, bool uppercase) { + + char *tmp = (char *)buf; + size_t i; + memset(tmp, 0x00, hex_max_len); + + int maxLen = ( hex_len > hex_max_len) ? hex_max_len : hex_len; + + for (i = 0; i < maxLen; ++i, tmp += 2 + spaces_between) { + sprintf(tmp, (uppercase) ? "%02X" : "%02x", (unsigned int) hex_data[i]); + + for (int j = 0; j < spaces_between; j++) + sprintf(tmp + 2 + j, " "); + } + + i *= (2 + spaces_between); + int minStrLen = min_str_len > i ? min_str_len : 0; + if (minStrLen > hex_max_len) + minStrLen = hex_max_len; + for(; i < minStrLen; i++, tmp += 1) + sprintf(tmp, " "); + + return; +} + // printing and converting functions -void print_hex(const uint8_t * data, const size_t len) -{ - size_t i; - - for (i=0; i < len; i++) - printf("%02x ", data[i]); - - printf("\n"); -} - -void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) { - - int rownum = 0; - printf("[%02d] | ", rownum); - for (int i = 0; i < len; ++i) { - - printf("%02X ", data[i]); - - // check if a line break is needed - if ( breaks > 0 && !((i+1) % breaks) && (i+1 < len) ) { - ++rownum; - printf("\n[%02d] | ", rownum); - } - } - printf("\n"); -} - char *sprint_hex(const uint8_t *data, const size_t len) { + static char buf[1025] = {0}; - int maxLen = ( len > 1024/3) ? 1024/3 : len; - static char buf[1024]; - memset(buf, 0x00, 1024); - char *tmp = buf; - size_t i; - - for (i=0; i < maxLen; ++i, tmp += 3) - sprintf(tmp, "%02x ", (unsigned int) data[i]); + hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, 1, false); return buf; } +char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { + static char buf[1025] = {0}; + + hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, false); + + return buf; +} + +char *sprint_hex_inrow(const uint8_t *data, const size_t len) { + return sprint_hex_inrow_ex(data, len, 0); +} + char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks) { // make sure we don't go beyond our char array memory int max_len; @@ -189,28 +193,7 @@ char *sprint_bin(const uint8_t *data, const size_t len) { return sprint_bin_break(data, len, 0); } -char *sprint_hex_ascii(const uint8_t *data, const size_t len) { - static char buf[1024]; - char *tmp = buf; - memset(buf, 0x00, 1024); - size_t max_len = (len > 255) ? 255 : len; - // max 255 bytes * 3 + 2 characters = 767 in buffer - sprintf(tmp, "%s| ", sprint_hex(data, max_len) ); - - size_t i = 0; - size_t pos = (max_len * 3)+2; - // add another 255 characters ascii = 1020 characters of buffer used - while(i < max_len) { - char c = data[i]; - if ( (c < 32) || (c == 127)) - c = '.'; - sprintf(tmp+pos+i, "%c", c); - ++i; - } - return buf; -} - -char *sprint_ascii(const uint8_t *data, const size_t len) { +char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { static char buf[1024]; char *tmp = buf; memset(buf, 0x00, 1024); @@ -221,6 +204,11 @@ char *sprint_ascii(const uint8_t *data, const size_t len) { tmp[i] = ((c < 32) || (c == 127)) ? '.' : c; ++i; } + + int minStrLen = min_str_len > i ? min_str_len : 0; + for(; i < minStrLen; ++i) + tmp[i] = ' '; + return buf; } @@ -284,16 +272,6 @@ uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockS return tmp; } -// takes a uint8_t src array, for len items and reverses the byte order in blocksizes (8,16,32,64), -// returns: the dest array contains the reordered src array. -void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t blockSize, uint8_t *dest){ - for (uint8_t block=0; block < (uint8_t)(len/blockSize); block++){ - for (size_t i = 0; i < blockSize; i++){ - dest[i+(blockSize*block)] = src[(blockSize-1-i)+(blockSize*block)]; - } - } -} - //assumes little endian char * printBits(size_t const size, void const * const ptr) { @@ -316,13 +294,30 @@ char * printBits(size_t const size, void const * const ptr) return buf; } +char * printBitsPar(const uint8_t *b, size_t len) { + static char buf1[512] = {0}; + static char buf2[512] = {0}; + static char *buf; + if (buf != buf1) + buf = buf1; + else + buf = buf2; + memset(buf, 0x00, 512); + + for (int i = 0; i < len; i++) { + buf[i] = ((b[i / 8] << (i % 8)) & 0x80) ? '1':'0'; + } + return buf; +} + + // ------------------------------------------------------------------------- // string parameters lib // ------------------------------------------------------------------------- // ------------------------------------------------------------------------- // line - param line -// bg, en - symbol numbers in param line of beginning an ending parameter +// bg, en - symbol numbers in param line of beginning and ending parameter // paramnum - param number (from 0) // ------------------------------------------------------------------------- int param_getptr(const char *line, int *bg, int *en, int paramnum) @@ -355,13 +350,28 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) } -char param_getchar(const char *line, int paramnum) +int param_getlength(const char *line, int paramnum) { int bg, en; + if (param_getptr(line, &bg, &en, paramnum)) return 0; + + return en - bg + 1; +} + +char param_getchar(const char *line, int paramnum) { + return param_getchar_indx(line, 0, paramnum); +} + +char param_getchar_indx(const char *line, int indx, int paramnum) { + int bg, en; + if (param_getptr(line, &bg, &en, paramnum)) return 0x00; - return line[bg]; + if (bg + indx > en) + return '\0'; + + return line[bg + indx]; } uint8_t param_get8(const char *line, int paramnum) @@ -441,7 +451,7 @@ int param_gethex(const char *line, int paramnum, uint8_t * data, int hexcnt) return 1; for(i = 0; i < hexcnt; i += 2) { - if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1])) ) return 1; + if (!(isxdigit((unsigned char)line[bg + i]) && isxdigit((unsigned char)line[bg + i + 1])) ) return 1; sscanf((char[]){line[bg + i], line[bg + i + 1], 0}, "%X", &temp); data[i / 2] = temp & 0xff; @@ -463,7 +473,7 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt) return 1; for(i = 0; i < *hexcnt; i += 2) { - if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1])) ) return 1; + if (!(isxdigit((unsigned char)line[bg + i]) && isxdigit((unsigned char)line[bg + i + 1])) ) return 1; sscanf((char[]){line[bg + i], line[bg + i + 1], 0}, "%X", &temp); data[i / 2] = temp & 0xff; @@ -471,11 +481,66 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt) return 0; } -int param_getstr(const char *line, int paramnum, char * str) + +int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxdatalen, int *datalen) { + int bg, en; + uint32_t temp; + char buf[5] = {0}; + + if (param_getptr(line, &bg, &en, paramnum)) return 1; + + *datalen = 0; + + int indx = bg; + while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { + indx++; + continue; + } + + if (isxdigit((unsigned char)line[indx])) { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = line[indx]; + } else { + // if we have symbols other than spaces and hex + return 1; + } + + if (*datalen >= maxdatalen) { + // if we dont have space in buffer and have symbols to translate + return 2; + } + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[*datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + (*datalen)++; + } + + indx++; + } + + if (strlen(buf) > 0) + //error when not completed hex bytes + return 3; + + return 0; +} + +int param_getstr(const char *line, int paramnum, char * str, size_t buffersize) { int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) return 0; + if (param_getptr(line, &bg, &en, paramnum)) { + return 0; + } + + // Prevent out of bounds errors + if (en - bg + 1 >= buffersize) { + printf("out of bounds error: want %d bytes have %zd bytes\n", en - bg + 1 + 1, buffersize); + return 0; + } memcpy(str, line + bg, en - bg + 1); str[en - bg + 1] = 0; @@ -493,6 +558,7 @@ https://github.com/ApertureLabsLtd/RFIDler/blob/master/firmware/Pic32/RFIDler.X/ int hextobinarray(char *target, char *source) { int length, i, count= 0; + char* start = source; char x; length = strlen(source); @@ -508,8 +574,10 @@ int hextobinarray(char *target, char *source) x -= '0'; else if (x >= 'A' && x <= 'F') x -= 'A' - 10; - else + else { + printf("Discovered unknown character %c %d at idx %tu of %s\n", x, x, source - start, start); return 0; + } // output for(i= 0 ; i < 4 ; ++i, ++count) *(target++)= (x >> (3 - i)) & 1; @@ -518,17 +586,6 @@ int hextobinarray(char *target, char *source) return count; } -// convert hex to human readable binary string -int hextobinstring(char *target, char *source) -{ - int length; - - if(!(length= hextobinarray(target, source))) - return 0; - binarraytobinstring(target, target, length); - return length; -} - // convert binary array of 0x00/0x01 values to hex (safe to do in place as target will always be shorter than source) // return number of bits converted int binarraytohex(char *target,char *source, int length) @@ -551,16 +608,6 @@ int binarraytohex(char *target,char *source, int length) return length; } -// convert binary array to human readable binary -void binarraytobinstring(char *target, char *source, int length) -{ - int i; - - for(i= 0 ; i < length ; ++i) - *(target++)= *(source++) + '0'; - *target= '\0'; -} - // return parity bit required to match type uint8_t GetParity( uint8_t *bits, uint8_t type, int length) { @@ -588,13 +635,6 @@ void xor(unsigned char *dst, unsigned char *src, size_t len) { *dst ^= *src; } -int32_t le24toh (uint8_t data[3]) { - return (data[2] << 16) | (data[1] << 8) | data[0]; -} -uint32_t le32toh (uint8_t *data) { - return (uint32_t)( (data[3]<<24) | (data[2]<<16) | (data[1]<<8) | data[0]); -} - // RotateLeft - Ultralight, Desfire, works on byte level // 00-01-02 >> 01-02-00 void rol(uint8_t *data, const size_t len){ @@ -614,7 +654,28 @@ void clean_ascii(unsigned char *buf, size_t len) { } } +// replace \r \n to \0 +void strcleanrn(char *buf, size_t len) { + strcreplace(buf, len, '\n', '\0'); + strcreplace(buf, len, '\r', '\0'); +} +// replace char in buffer +void strcreplace(char *buf, size_t len, char from, char to) { + for (size_t i = 0; i < len; i++) { + if (buf[i] == from) + buf[i] = to; + } +} + +char *strmcopy(char *buf) { + char * str = NULL; + if ((str = (char*) malloc(strlen(buf) + 1)) != NULL) { + memset(str, 0, strlen(buf) + 1); + strcpy(str, buf); + } + return str; +} // determine number of logical CPU cores (use for multithreaded functions) diff --git a/client/util.h b/client/util.h index 640ef434..31ad29fb 100644 --- a/client/util.h +++ b/client/util.h @@ -12,6 +12,7 @@ #define UTIL_H__ #include +#include #include #ifndef ROTR @@ -23,10 +24,17 @@ #ifndef MAX # define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +#ifndef arraylen +#define arraylen(x) (sizeof(x)/sizeof((x)[0])) +#endif #define EVEN 0 #define ODD 1 +#ifndef FILE_PATH_SIZE +#define FILE_PATH_SIZE 2000 +#endif + extern int ukbhit(void); extern void AddLogLine(char *fileName, char *extData, char *c); @@ -35,23 +43,28 @@ extern void AddLogUint64(char *fileName, char *extData, const uint64_t data); extern void AddLogCurrentDT(char *fileName); extern void FillFileNameByUID(char *fileName, uint8_t * uid, char *ext, int byteCount); -extern void print_hex(const uint8_t * data, const size_t len); +extern void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, + const size_t hex_max_len, const size_t min_str_len, const size_t spaces_between, bool uppercase); + extern char *sprint_hex(const uint8_t * data, const size_t len); +extern char *sprint_hex_inrow(const uint8_t *data, const size_t len); +extern char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len); extern char *sprint_bin(const uint8_t * data, const size_t len); extern char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks); -extern char *sprint_hex_ascii(const uint8_t *data, const size_t len); -extern char *sprint_ascii(const uint8_t *data, const size_t len); +extern char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len); extern void num_to_bytes(uint64_t n, size_t len, uint8_t* dest); extern uint64_t bytes_to_num(uint8_t* src, size_t len); extern void num_to_bytebits(uint64_t n, size_t len, uint8_t *dest); extern void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest); extern char *printBits(size_t const size, void const * const ptr); +extern char * printBitsPar(const uint8_t *b, size_t len); extern uint32_t SwapBits(uint32_t value, int nrbits); extern uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockSize); -extern void SwapEndian64ex(const uint8_t *src, const size_t len, const uint8_t blockSize, uint8_t *dest); +extern int param_getlength(const char *line, int paramnum); extern char param_getchar(const char *line, int paramnum); +extern char param_getchar_indx(const char *line, int indx, int paramnum); extern int param_getptr(const char *line, int *bg, int *en, int paramnum); extern uint8_t param_get8(const char *line, int paramnum); extern uint8_t param_get8ex(const char *line, int paramnum, int deflt, int base); @@ -61,21 +74,21 @@ extern uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination extern uint8_t param_isdec(const char *line, int paramnum); extern int param_gethex(const char *line, int paramnum, uint8_t * data, int hexcnt); extern int param_gethex_ex(const char *line, int paramnum, uint8_t * data, int *hexcnt); -extern int param_getstr(const char *line, int paramnum, char * str); +extern int param_gethex_to_eol(const char *line, int paramnum, uint8_t * data, int maxdatalen, int *datalen); +extern int param_getstr(const char *line, int paramnum, char * str, size_t buffersize); extern int hextobinarray( char *target, char *source); -extern int hextobinstring( char *target, char *source); extern int binarraytohex( char *target, char *source, int length); -extern void binarraytobinstring(char *target, char *source, int length); extern uint8_t GetParity( uint8_t *string, uint8_t type, int length); extern void wiegand_add_parity(uint8_t *target, uint8_t *source, uint8_t length); extern void xor(unsigned char *dst, unsigned char *src, size_t len); -extern int32_t le24toh(uint8_t data[3]); -extern uint32_t le32toh (uint8_t *data); extern void rol(uint8_t *data, const size_t len); extern void clean_ascii(unsigned char *buf, size_t len); +void strcleanrn(char *buf, size_t len); +void strcreplace(char *buf, size_t len, char from, char to); +char *strmcopy(char *buf); extern int num_CPUs(void); // number of logical CPUs diff --git a/client/util_darwin.h b/client/util_darwin.h new file mode 100644 index 00000000..dd159ec5 --- /dev/null +++ b/client/util_darwin.h @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// (c) 2018 AntiCat +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// macOS framework bindings +//----------------------------------------------------------------------------- + +#ifndef UTIL_DARWIN_H__ +#define UTIL_DARWIN_H__ + +void disableAppNap(const char* reason); +void enableAppNap(); + +void makeUnfocusable(); +void makeFocusable(); + +#endif \ No newline at end of file diff --git a/client/util_darwin.m b/client/util_darwin.m new file mode 100644 index 00000000..c0f69aa9 --- /dev/null +++ b/client/util_darwin.m @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// (c) 2018 AntiCat +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// macOS framework bindings +//----------------------------------------------------------------------------- + +#import "util_darwin.h" + +#import +#import +#import + +static id activity = nil; + +//OS X Version 10.10 is defined in OS X 10.10 and later +#if defined(MAC_OS_X_VERSION_10_10) +void disableAppNap(const char* reason) { + if(activity == nil) { + //NSLog(@"disableAppNap: %@", @(reason)); + activity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityBackground reason:@(reason)]; + [activity retain]; + } +} + +void enableAppNap() { + if(activity != nil) { + //NSLog(@"enableAppNap"); + [[NSProcessInfo processInfo] endActivity:activity]; + [activity release]; + activity = nil; + } +} + +#else +void disableAppNap(const char* reason) { } +void enableAppNap() { } +#endif + +//OS X Version 10.6 is defined in OS X 10.6 and later +#if defined(MAC_OS_X_VERSION_10_6) +void makeUnfocusable() { + [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited]; +} + +void makeFocusable() { + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; +} +#else +void makeUnfocusable() { } +void makeFocusable() { } +#endif diff --git a/client/util_posix.c b/client/util_posix.c index dd3d714c..435e41f3 100644 --- a/client/util_posix.c +++ b/client/util_posix.c @@ -35,10 +35,14 @@ void msleep(uint32_t n) { } #endif // _WIN32 -#ifdef __MACH__ +#ifdef __APPLE__ +#ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC (1) +#endif +#ifndef CLOCK_REALTIME #define CLOCK_REALTIME (2) +#endif #include #include @@ -63,20 +67,18 @@ void msleep(uint32_t n) { } else if (clk_id == CLOCK_MONOTONIC) { static uint64_t clock_start_time = 0; - static mach_timebase_info_data_t timebase_ifo = {0, 0}; + static mach_timebase_info_data_t timebase_info = {0, 0}; uint64_t now = mach_absolute_time(); if (clock_start_time == 0) { - //kern_return_t mach_status = mach_timebase_info(&timebase_ifo); - // appease "unused variable" warning for release builds - //(void)mach_status; + mach_timebase_info(&timebase_info); clock_start_time = now; } now = (uint64_t)((double)(now - clock_start_time) - * (double)timebase_ifo.numer - / (double)timebase_ifo.denom); + * (double)timebase_info.numer + / (double)timebase_info.denom); t->tv_sec = now / 1000000000; t->tv_nsec = now % 1000000000; @@ -115,7 +117,7 @@ uint64_t msclock() { #include struct _timeb t; _ftime(&t); - return 1000 * t.time + t.millitm; + return 1000 * (uint64_t)t.time + t.millitm; // NORMAL CODE (use _ftime_s) //struct _timeb t; diff --git a/common/Makefile.common b/common/Makefile.common index 29b72a4c..0ab89b3d 100644 --- a/common/Makefile.common +++ b/common/Makefile.common @@ -29,7 +29,7 @@ GZIP=gzip OBJDIR = obj -INCLUDE = -I../include -I../common +INCLUDE = -I../include -I../common -I. TAR=tar TARFLAGS = -C .. -rvf @@ -75,7 +75,7 @@ LIBS = -lgcc THUMBOBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(THUMBSRC))) ARMOBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(ARMSRC))) ASMOBJ = $(patsubst %.s,$(OBJDIR)/%.o,$(notdir $(ASMSRC))) -VERSIONOBJ = $(OBJDIR)/version.o +VERSIONOBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(VERSIONSRC))) $(THUMBOBJ): $(OBJDIR)/%.o: %.c $(INCLUDES) $(CC) $(CFLAGS) -mthumb -mthumb-interwork -o $@ $< @@ -99,11 +99,6 @@ OBJCOPY_TRANSLATIONS = --no-change-warnings \ $(OBJDIR)/%.s19: $(OBJDIR)/%.elf $(OBJCOPY) -Osrec --srec-forceS3 --strip-debug $(OBJCOPY_TRANSLATIONS) $^ $@ -# version.c should be remade on every compilation -.PHONY: version.c -version.c: default_version.c - perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ - # Automatic dependency generation DEPENDENCY_FILES = $(patsubst %.c,$(OBJDIR)/%.d,$(notdir $(THUMBSRC))) \ $(patsubst %.c,$(OBJDIR)/%.d,$(notdir $(ARMSRC))) \ diff --git a/common/Makefile_Enabled_Options.common b/common/Makefile_Enabled_Options.common new file mode 100644 index 00000000..8774946e --- /dev/null +++ b/common/Makefile_Enabled_Options.common @@ -0,0 +1,42 @@ +#NOTES: +# Do not put any comments inside the definition below (before the final flag) +# All definition lines except the last must end in a \ +# +#BEGIN +APP_CFLAGS += -DWITH_ISO14443a_StandAlone \ + -DWITH_LF\ + -DWITH_ISO15693 \ + -DWITH_ISO14443a \ + -DWITH_ISO14443b \ + -DWITH_ICLASS \ + -DWITH_LEGICRF \ + -DWITH_HITAG \ + -DWITH_CRC \ + -DWITH_HFSNOOP \ + -DWITH_GUI +#END + + +### Standalone modes: +#-DWITH_ISO14443a_StandAlone +#-DWITH_LF_StandAlone +# +# if both WITH_LF_StandAlone and WITH_ISO4443a_StandAlone are defined +# ISO14443a_StandAlone will be the only one that runs +# You must remove it and define WITH_LF_StandAlone for LF standalone mode + +### Other options: +#-DWITH_LF \ include LF support in build +#-DWITH_ISO15693 \ include ISO15693 support in build +#-DWITH_ISO14443a \ include ISO14443a support in build +#-DWITH_ISO14443b \ include ISO14443b support in build +#-DWITH_ICLASS \ include ICLASS support in build +#-DWITH_LEGICRF \ include LEGIC support in build +#-DWITH_HITAG \ include HITAG support in build +#-DWITH_CRC \ include CRC support in build +#-DWITH_HFSNOOP \ include HFSNOOP support in build +#-DWITH_SMARTCARD \ include SMARTCARD support in build +#-DWITH_GUI \ include QT GUI/Graph support in build +#-DWITH_LCD \ include LCD support in build (experimental?) + +#marshmellow NOTE: tested GUI, and SMARTCARD removal only... diff --git a/common/crapto1/crapto1.c b/common/crapto1/crapto1.c index 9398a1f3..1edfca1b 100644 --- a/common/crapto1/crapto1.c +++ b/common/crapto1/crapto1.c @@ -426,7 +426,6 @@ int nonce_distance(uint32_t from, uint32_t to) return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; } - static uint32_t fastfwd[2][8] = { { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; diff --git a/common/fpga.h b/common/fpga.h new file mode 100644 index 00000000..b99a7593 --- /dev/null +++ b/common/fpga.h @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------------- +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- + +#ifndef __FPGA_H +#define __FPGA_H + +#define FPGA_BITSTREAM_FIXED_HEADER_SIZE sizeof(bitparse_fixed_header) +#define FPGA_INTERLEAVE_SIZE 288 +#define FPGA_CONFIG_SIZE 42336L // our current fpga_[lh]f.bit files are 42175 bytes. Rounded up to next multiple of FPGA_INTERLEAVE_SIZE + +static const uint8_t bitparse_fixed_header[] = {0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01}; +extern const int fpga_bitstream_num; +extern const char* const fpga_version_information[]; + +#endif diff --git a/common/iso15693tools.c b/common/iso15693tools.c index 26e636ca..f1214458 100644 --- a/common/iso15693tools.c +++ b/common/iso15693tools.c @@ -11,6 +11,12 @@ #include #include //#include "iso15693tools.h" +#ifdef ON_DEVICE +#include "printf.h" +#else +#include +#endif + #define POLY 0x8408 @@ -51,8 +57,6 @@ int Iso15693AddCrc(uint8_t *req, int n) { } -int sprintf(char *str, const char *format, ...); - // returns a string representation of the UID // UID is transmitted and stored LSB first, displayed MSB first // target char* buffer, where to put the UID, if NULL a static buffer is returned diff --git a/common/iso15693tools.h b/common/iso15693tools.h index ec63728f..96095fba 100644 --- a/common/iso15693tools.h +++ b/common/iso15693tools.h @@ -5,67 +5,9 @@ #define ISO15693_H__ // ISO15693 CRC -#define ISO15_CRC_PRESET (uint16_t)0xFFFF -#define ISO15_CRC_POLY (uint16_t)0x8408 -#define ISO15_CRC_CHECK ((uint16_t)(~0xF0B8 & 0xFFFF)) // use this for checking of a correct crc - -// REQUEST FLAGS - -#define ISO15_REQ_SUBCARRIER_SINGLE 0x00 // Tag should respond using one subcarrier (ASK) -#define ISO15_REQ_SUBCARRIER_TWO 0x01 // Tag should respond using two subcarriers (FSK) -#define ISO15_REQ_DATARATE_LOW 0x00 // Tag should respond using low data rate -#define ISO15_REQ_DATARATE_HIGH 0x02 // Tag should respond using high data rate -#define ISO15_REQ_NONINVENTORY 0x00 -#define ISO15_REQ_INVENTORY 0x04 // This is an inventory request - see inventory flags -#define ISO15_REQ_PROTOCOL_NONEXT 0x00 -#define ISO15_REQ_PROTOCOL_EXT 0x08 // RFU - -// REQUEST FLAGS when INVENTORY is not set - -#define ISO15_REQ_SELECT 0x10 // only selected cards response -#define ISO15_REQ_ADDRESS 0x20 // this req contains an address -#define ISO15_REQ_OPTION 0x40 // Command specific option selector - -//REQUEST FLAGS when INVENTORY is set - -#define ISO15_REQINV_AFI 0x10 // AFI Field is present -#define ISO15_REQINV_SLOT1 0x20 // 1 Slot -#define ISO15_REQINV_SLOT16 0x00 // 16 Slots -#define ISO15_REQINV_OPTION 0x40 // Command specific option selector - -//RESPONSE FLAGS -#define ISO15_RES_ERROR 0x01 -#define ISO15_RES_EXT 0x08 // Protocol Extention - -// RESPONSE ERROR CODES -#define ISO15_NOERROR 0x00 -#define ISO15_ERROR_CMD_NOT_SUP 0x01 // Command not supported -#define ISO15_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error) -#define ISO15_ERROR_CMD_OPTION 0x03 // Command option not supported -#define ISO15_ERROR_GENERIC 0x0F // No additional Info about this error -#define ISO15_ERROR_BLOCK_UNAVAILABLE 0x10 -#define ISO15_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again -#define ISO15_ERROR_BLOCK_LOCKED 0x12 // cannot be changed -#define ISO15_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful -#define ISO15_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful - -// COMMAND CODES -#define ISO15_CMD_INVENTORY 0x01 -#define ISO15_CMD_STAYQUIET 0x02 -#define ISO15_CMD_READ 0x20 -#define ISO15_CMD_WRITE 0x21 -#define ISO15_CMD_LOCK 0x22 -#define ISO15_CMD_READMULTI 0x23 -#define ISO15_CMD_WRITEMULTI 0x24 -#define ISO15_CMD_SELECT 0x25 -#define ISO15_CMD_RESET 0x26 -#define ISO15_CMD_WRITEAFI 0x27 -#define ISO15_CMD_LOCKAFI 0x28 -#define ISO15_CMD_WRITEDSFID 0x29 -#define ISO15_CMD_LOCKDSFID 0x2A -#define ISO15_CMD_SYSINFO 0x2B -#define ISO15_CMD_SECSTATUS 0x2C - +#define ISO15693_CRC_PRESET (uint16_t)0xFFFF +#define ISO15693_CRC_POLY (uint16_t)0x8408 +#define ISO15693_CRC_CHECK ((uint16_t)(~0xF0B8 & 0xFFFF)) // use this for checking of a correct crc uint16_t Iso15693Crc(uint8_t *v, int n); int Iso15693AddCrc(uint8_t *req, int n); diff --git a/common/lfdemod.c b/common/lfdemod.c index f470371a..76900047 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -10,8 +10,8 @@ // // NOTES: // LF Demod functions are placed here to allow the flexability to use client or -// device side. Most BUT NOT ALL of these functions are currenlty safe for -// device side use currently. (DetectST for example...) +// device side. Most BUT NOT ALL of these functions are currently safe for +// device side use. (DetectST for example...) // // There are likely many improvements to the code that could be made, please // make suggestions... diff --git a/common/parity.h b/common/parity.h index 615fdeee..c574db55 100644 --- a/common/parity.h +++ b/common/parity.h @@ -13,6 +13,7 @@ #include #include +#include "string.h" extern const uint8_t OddByteParity[256]; @@ -21,6 +22,11 @@ static inline bool oddparity8(const uint8_t x) { return OddByteParity[x]; } +static inline void oddparitybuf(const uint8_t *x, size_t len, uint8_t *parity) { + memset(parity, 0x00, (len - 1) / 8 + 1); + for (int i = 0; i < len; i++) + parity[i / 8] |= oddparity8(x[i]) << (7 - (i % 8)); +} static inline bool evenparity8(const uint8_t x) { return !OddByteParity[x]; diff --git a/common/polarssl/aes.h b/common/polarssl/aes.h index 946bd87d..299cb4cd 100644 --- a/common/polarssl/aes.h +++ b/common/polarssl/aes.h @@ -125,7 +125,6 @@ int aes_crypt_ecb( aes_context *ctx, int mode, const unsigned char input[16], unsigned char output[16] ); - #if defined(POLARSSL_CIPHER_MODE_CBC) /** * \brief AES-CBC buffer encryption/decryption diff --git a/common/polarssl/aes_cmac128.c b/common/polarssl/aes_cmac128.c new file mode 100644 index 00000000..595f89b8 --- /dev/null +++ b/common/polarssl/aes_cmac128.c @@ -0,0 +1,322 @@ +/* + * AES-CMAC from NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. + * + * Copyright (C) 2006-2014, Brainspark B.V. + * Copyright (C) 2014, Anargyros Plemenos + * Tests added Merkok, 2018 + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Reference : https://polarssl.org/discussions/generic/authentication-token + * NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. + * Tests here: + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf +*/ + +#include "polarssl/aes_cmac128.h" +#include + +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define _MSB(x) (((x)[0] & 0x80)?1:0) + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl_config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_AES_C) +#include "aes.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + + +/** + * zero a structure + */ +#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) + +/** + * zero a structure given a pointer to the structure + */ +#define ZERO_STRUCTP(x) do{ if((x) != NULL) memset((char *)(x), 0, sizeof(*(x)));} while(0) + + +/* For CMAC Calculation */ +static unsigned char const_Rb[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 +}; +static unsigned char const_Zero[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static inline void aes_cmac_128_left_shift_1(const uint8_t in[16], uint8_t out[16]) +{ + uint8_t overflow = 0; + int8_t i; + + for (i = 15; i >= 0; i--) { + out[i] = in[i] << 1; + out[i] |= overflow; + overflow = _MSB(&in[i]); + } +} + +static inline void aes_cmac_128_xor(const uint8_t in1[16], const uint8_t in2[16], + uint8_t out[16]) +{ + uint8_t i; + + for (i = 0; i < 16; i++) { + out[i] = in1[i] ^ in2[i]; + } +} + +/* + * AES-CMAC-128 context setup + */ +void aes_cmac128_starts(aes_cmac128_context *ctx, const uint8_t K[16]) +{ + uint8_t L[16]; + + /* Zero struct of aes_context */ + ZERO_STRUCTP(ctx); + /* Initialize aes_context */ + aes_setkey_enc(&ctx->aes_key, K, 128); + + /* step 1 - generate subkeys k1 and k2 */ + aes_crypt_ecb(&ctx->aes_key, AES_ENCRYPT, const_Zero, L); + + if (_MSB(L) == 0) { + aes_cmac_128_left_shift_1(L, ctx->K1); + } else { + uint8_t tmp_block[16]; + + aes_cmac_128_left_shift_1(L, tmp_block); + aes_cmac_128_xor(tmp_block, const_Rb, ctx->K1); + ZERO_STRUCT(tmp_block); + } + + if (_MSB(ctx->K1) == 0) { + aes_cmac_128_left_shift_1(ctx->K1, ctx->K2); + } else { + uint8_t tmp_block[16]; + + aes_cmac_128_left_shift_1(ctx->K1, tmp_block); + aes_cmac_128_xor(tmp_block, const_Rb, ctx->K2); + ZERO_STRUCT(tmp_block); + } + + ZERO_STRUCT(L); +} + +/* + * AES-CMAC-128 process message + */ +void aes_cmac128_update(aes_cmac128_context *ctx, const uint8_t *_msg, size_t _msg_len) +{ + uint8_t tmp_block[16]; + uint8_t Y[16]; + const uint8_t *msg = _msg; + size_t msg_len = _msg_len; + + /* + * copy the remembered last block + */ + ZERO_STRUCT(tmp_block); + if (ctx->last_len) { + memcpy(tmp_block, ctx->last, ctx->last_len); + } + + /* + * check if we expand the block + */ + if (ctx->last_len < 16) { + size_t len = MIN(16 - ctx->last_len, msg_len); + + memcpy(&tmp_block[ctx->last_len], msg, len); + memcpy(ctx->last, tmp_block, 16); + msg += len; + msg_len -= len; + ctx->last_len += len; + } + + if (msg_len == 0) { + /* if it is still the last block, we are done */ + ZERO_STRUCT(tmp_block); + return; + } + + /* + * It is not the last block anymore + */ + ZERO_STRUCT(ctx->last); + ctx->last_len = 0; + + /* + * now checksum everything but the last block + */ + aes_cmac_128_xor(ctx->X, tmp_block, Y); + aes_crypt_ecb(&ctx->aes_key, AES_ENCRYPT, Y, ctx->X); + + while (msg_len > 16) { + memcpy(tmp_block, msg, 16); + msg += 16; + msg_len -= 16; + + aes_cmac_128_xor(ctx->X, tmp_block, Y); + aes_crypt_ecb(&ctx->aes_key, AES_ENCRYPT, Y, ctx->X); + } + + /* + * copy the last block, it will be processed in + * aes_cmac128_final(). + */ + memcpy(ctx->last, msg, msg_len); + ctx->last_len = msg_len; + + ZERO_STRUCT(tmp_block); + ZERO_STRUCT(Y); +} + +/* + * AES-CMAC-128 compute T + */ +void aes_cmac128_final(aes_cmac128_context *ctx, uint8_t T[16]) +{ + uint8_t tmp_block[16]; + uint8_t Y[16]; + + if (ctx->last_len < 16) { + ctx->last[ctx->last_len] = 0x80; + aes_cmac_128_xor(ctx->last, ctx->K2, tmp_block); + } else { + aes_cmac_128_xor(ctx->last, ctx->K1, tmp_block); + } + + aes_cmac_128_xor(tmp_block, ctx->X, Y); + aes_crypt_ecb(&ctx->aes_key, AES_ENCRYPT, Y, T); + + ZERO_STRUCT(tmp_block); + ZERO_STRUCT(Y); + ZERO_STRUCTP(ctx); +} + +/* + * Checkup routine + * + * https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf + */ +int aes_cmac_self_test( int verbose ) +{ + unsigned char key[16] = {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}; + unsigned char mac[16] = {0}; + aes_cmac128_context ctx; + int ret; + + // check Example1: + if( verbose != 0 ) + polarssl_printf( " AES-CMAC-128 zero length data: " ); + unsigned char ex1data[16] = {0}; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, ex1data, 0); + aes_cmac128_final(&ctx, mac); + unsigned char ex1res[16] = {0xBB, 0x1D, 0x69, 0x29, 0xE9, 0x59, 0x37, 0x28, 0x7F, 0xA3, 0x7D, 0x12, 0x9B, 0x75, 0x67, 0x46}; + if(!memcmp(mac, ex1res, 16)) { + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } else { + polarssl_printf( "failed\n" ); + ret = 1; + goto exit; + } + + // check Example2: + if( verbose != 0 ) + polarssl_printf( " AES-CMAC-128 one block data : " ); + unsigned char ex2data[16] = {0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A}; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, ex2data, sizeof(ex2data)); + aes_cmac128_final(&ctx, mac); + unsigned char ex2res[16] = {0x07, 0x0A, 0x16, 0xB4, 0x6B, 0x4D, 0x41, 0x44, 0xF7, 0x9B, 0xDD, 0x9D, 0xD0, 0x4A, 0x28, 0x7C}; + if(!memcmp(mac, ex2res, 16)) { + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } else { + polarssl_printf( "failed\n" ); + ret = 1; + goto exit; + } + + // check Example3: + if( verbose != 0 ) + polarssl_printf( " AES-CMAC-128 20 bytes of data: " ); + unsigned char ex3data[20] = {0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57}; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, ex3data, sizeof(ex3data)); + aes_cmac128_final(&ctx, mac); + unsigned char ex3res[16] = {0x7D, 0x85, 0x44, 0x9E, 0xA6, 0xEA, 0x19, 0xC8, 0x23, 0xA7, 0xBF, 0x78, 0x83, 0x7D, 0xFA, 0xDE}; + if(!memcmp(mac, ex3res, 16)) { + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } else { + polarssl_printf( "failed\n" ); + ret = 1; + goto exit; + } + + // check Example4: + if( verbose != 0 ) + polarssl_printf( " AES-CMAC-128 4 blocks of data: " ); + unsigned char ex4data[64] = {0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10}; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, ex4data, sizeof(ex4data)); + aes_cmac128_final(&ctx, mac); + unsigned char ex4res[16] = {0x51, 0xF0, 0xBE, 0xBF, 0x7E, 0x3B, 0x9D, 0x92, 0xFC, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3C, 0xFE}; + if(!memcmp(mac, ex4res, 16)) { + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } else { + polarssl_printf( "failed\n" ); + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + ret = 0; + +exit: + return( ret ); +} + diff --git a/common/polarssl/aes_cmac128.h b/common/polarssl/aes_cmac128.h new file mode 100644 index 00000000..b792755f --- /dev/null +++ b/common/polarssl/aes_cmac128.h @@ -0,0 +1,81 @@ +/* + * AES-CMAC from NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. + * + * Copyright (C) 2006-2014, Brainspark B.V. + * Copyright (C) 2014, Anargyros Plemenos + * Tests added Merkok, 2018 + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Reference : https://polarssl.org/discussions/generic/authentication-token + * NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. + * Tests here: + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CMAC.pdf +*/ + +#include +#include +#include "aes.h" + +typedef struct aes_cmac_128_context { + aes_context aes_key; + + uint8_t K1[16]; + uint8_t K2[16]; + + uint8_t X[16]; + + uint8_t last[16]; + size_t last_len; +} +aes_cmac128_context; + +/* + * \brief AES-CMAC-128 context setup + * + * \param ctx context to be initialized + * \param key secret key for AES-128 + */ +void aes_cmac128_starts(aes_cmac128_context *ctx, const uint8_t K[16]); + +/* + * \brief AES-CMAC-128 process message + * + * \param ctx context to be initialized + * \param _msg the given message + * \param _msg_len the length of message + */ +void aes_cmac128_update(aes_cmac128_context *ctx, const uint8_t *_msg, size_t _msg_len); + +/* + * \brief AES-CMAC-128 compute T + * + * \param ctx context to be initialized + * \param T the generated MAC which is used to validate the message + */ +void aes_cmac128_final(aes_cmac128_context *ctx, uint8_t T[16]); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int aes_cmac_self_test( int verbose ); + diff --git a/common/polarssl/bignum.c b/common/polarssl/bignum.c new file mode 100644 index 00000000..d22dd5c7 --- /dev/null +++ b/common/polarssl/bignum.c @@ -0,0 +1,2143 @@ +/* + * Multi-precision integer library + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * This MPI implementation is based on: + * + * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + * http://www.stillhq.com/extracted/gnupg-api/mpi/ + * http://math.libtomcrypt.com/files/tommath.pdf + */ + +#include "polarssl_config.h" + +#if defined(POLARSSL_BIGNUM_C) + +#include "bignum.h" +#include "bn_mul.h" + +#include + +#define ciL (sizeof(t_uint)) /* chars in limb */ +#define biL (ciL << 3) /* bits in limb */ +#define biH (ciL << 2) /* half limb size */ + +/* + * Convert between bits/chars and number of limbs + */ +#define BITS_TO_LIMBS(i) (((i) + biL - 1) / biL) +#define CHARS_TO_LIMBS(i) (((i) + ciL - 1) / ciL) + +/* + * Initialize one MPI + */ +void mpi_init( mpi *X ) +{ + if( X == NULL ) + return; + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Unallocate one MPI + */ +void mpi_free( mpi *X ) +{ + if( X == NULL ) + return; + + if( X->p != NULL ) + { + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Enlarge to the specified number of limbs + */ +int mpi_grow( mpi *X, size_t nblimbs ) +{ + t_uint *p; + + if( nblimbs > POLARSSL_MPI_MAX_LIMBS ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + if( X->n < nblimbs ) + { + if( ( p = (t_uint *) malloc( nblimbs * ciL ) ) == NULL ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + memset( p, 0, nblimbs * ciL ); + + if( X->p != NULL ) + { + memcpy( p, X->p, X->n * ciL ); + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->n = nblimbs; + X->p = p; + } + + return( 0 ); +} + +/* + * Copy the contents of Y into X + */ +int mpi_copy( mpi *X, const mpi *Y ) +{ + int ret; + size_t i; + + if( X == Y ) + return( 0 ); + + for( i = Y->n - 1; i > 0; i-- ) + if( Y->p[i] != 0 ) + break; + i++; + + X->s = Y->s; + + MPI_CHK( mpi_grow( X, i ) ); + + memset( X->p, 0, X->n * ciL ); + memcpy( X->p, Y->p, i * ciL ); + +cleanup: + + return( ret ); +} + +/* + * Swap the contents of X and Y + */ +void mpi_swap( mpi *X, mpi *Y ) +{ + mpi T; + + memcpy( &T, X, sizeof( mpi ) ); + memcpy( X, Y, sizeof( mpi ) ); + memcpy( Y, &T, sizeof( mpi ) ); +} + +/* + * Set value from integer + */ +int mpi_lset( mpi *X, t_sint z ) +{ + int ret; + + MPI_CHK( mpi_grow( X, 1 ) ); + memset( X->p, 0, X->n * ciL ); + + X->p[0] = ( z < 0 ) ? -z : z; + X->s = ( z < 0 ) ? -1 : 1; + +cleanup: + + return( ret ); +} + +/* + * Get a specific bit + */ +int mpi_get_bit( const mpi *X, size_t pos ) +{ + if( X->n * biL <= pos ) + return( 0 ); + + return ( X->p[pos / biL] >> ( pos % biL ) ) & 0x01; +} + +/* + * Set a bit to a specific value of 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ) +{ + int ret = 0; + size_t off = pos / biL; + size_t idx = pos % biL; + + if( val != 0 && val != 1 ) + return POLARSSL_ERR_MPI_BAD_INPUT_DATA; + + if( X->n * biL <= pos ) + { + if( val == 0 ) + return ( 0 ); + + MPI_CHK( mpi_grow( X, off + 1 ) ); + } + + X->p[off] = ( X->p[off] & ~( 0x01 << idx ) ) | ( val << idx ); + +cleanup: + + return( ret ); +} + +/* + * Return the number of least significant bits + */ +size_t mpi_lsb( const mpi *X ) +{ + size_t i, j, count = 0; + + for( i = 0; i < X->n; i++ ) + for( j = 0; j < biL; j++, count++ ) + if( ( ( X->p[i] >> j ) & 1 ) != 0 ) + return( count ); + + return( 0 ); +} + +/* + * Return the number of most significant bits + */ +size_t mpi_msb( const mpi *X ) +{ + size_t i, j; + + for( i = X->n - 1; i > 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = biL; j > 0; j-- ) + if( ( ( X->p[i] >> ( j - 1 ) ) & 1 ) != 0 ) + break; + + return( ( i * biL ) + j ); +} + +/* + * Return the total size in bytes + */ +size_t mpi_size( const mpi *X ) +{ + return( ( mpi_msb( X ) + 7 ) >> 3 ); +} + +/* + * Convert an ASCII character to digit value + */ +static int mpi_get_digit( t_uint *d, int radix, char c ) +{ + *d = 255; + + if( c >= 0x30 && c <= 0x39 ) *d = c - 0x30; + if( c >= 0x41 && c <= 0x46 ) *d = c - 0x37; + if( c >= 0x61 && c <= 0x66 ) *d = c - 0x57; + + if( *d >= (t_uint) radix ) + return( POLARSSL_ERR_MPI_INVALID_CHARACTER ); + + return( 0 ); +} + +/* + * Import from an ASCII string + */ +int mpi_read_string( mpi *X, int radix, const char *s ) +{ + int ret; + size_t i, j, slen, n; + t_uint d; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &T ); + + slen = strlen( s ); + + if( radix == 16 ) + { + n = BITS_TO_LIMBS( slen << 2 ); + + MPI_CHK( mpi_grow( X, n ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = slen, j = 0; i > 0; i--, j++ ) + { + if( i == 1 && s[i - 1] == '-' ) + { + X->s = -1; + break; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i - 1] ) ); + X->p[j / (2 * ciL)] |= d << ( (j % (2 * ciL)) << 2 ); + } + } + else + { + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = 0; i < slen; i++ ) + { + if( i == 0 && s[i] == '-' ) + { + X->s = -1; + continue; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + MPI_CHK( mpi_mul_int( &T, X, radix ) ); + + if( X->s == 1 ) + { + MPI_CHK( mpi_add_int( X, &T, d ) ); + } + else + { + MPI_CHK( mpi_sub_int( X, &T, d ) ); + } + } + } + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +/* + * Helper to write the digits high-order first + */ +static int mpi_write_hlp( mpi *X, int radix, char **p ) +{ + int ret; + t_uint r; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + MPI_CHK( mpi_mod_int( &r, X, radix ) ); + MPI_CHK( mpi_div_int( X, NULL, X, radix ) ); + + if( mpi_cmp_int( X, 0 ) != 0 ) + MPI_CHK( mpi_write_hlp( X, radix, p ) ); + + if( r < 10 ) + *(*p)++ = (char)( r + 0x30 ); + else + *(*p)++ = (char)( r + 0x37 ); + +cleanup: + + return( ret ); +} + +/* + * Export into an ASCII string + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ) +{ + int ret = 0; + size_t n; + char *p; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + n = mpi_msb( X ); + if( radix >= 4 ) n >>= 1; + if( radix >= 16 ) n >>= 1; + n += 3; + + if( *slen < n ) + { + *slen = n; + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + } + + p = s; + mpi_init( &T ); + + if( X->s == -1 ) + *p++ = '-'; + + if( radix == 16 ) + { + int c; + size_t i, j, k; + + for( i = X->n, k = 0; i > 0; i-- ) + { + for( j = ciL; j > 0; j-- ) + { + c = ( X->p[i - 1] >> ( ( j - 1 ) << 3) ) & 0xFF; + + if( c == 0 && k == 0 && ( i + j + 3 ) != 0 ) + continue; + + *(p++) = "0123456789ABCDEF" [c / 16]; + *(p++) = "0123456789ABCDEF" [c % 16]; + k = 1; + } + } + } + else + { + MPI_CHK( mpi_copy( &T, X ) ); + + if( T.s == -1 ) + T.s = 1; + + MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); + } + + *p++ = '\0'; + *slen = p - s; + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Read X from an opened file + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ) +{ + t_uint d; + size_t slen; + char *p; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + memset( s, 0, sizeof( s ) ); + if( fgets( s, sizeof( s ) - 1, fin ) == NULL ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + + slen = strlen( s ); + if( slen == sizeof( s ) - 2 ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + if( s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; } + if( s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; } + + p = s + slen; + while( --p >= s ) + if( mpi_get_digit( &d, radix, *p ) != 0 ) + break; + + return( mpi_read_string( X, radix, p + 1 ) ); +} + +/* + * Write X into an opened file (or stdout if fout == NULL) + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ) +{ + int ret; + size_t n, slen, plen; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + n = sizeof( s ); + memset( s, 0, n ); + n -= 2; + + MPI_CHK( mpi_write_string( X, radix, s, (size_t *) &n ) ); + + if( p == NULL ) p = ""; + + plen = strlen( p ); + slen = strlen( s ); + s[slen++] = '\r'; + s[slen++] = '\n'; + + if( fout != NULL ) + { + if( fwrite( p, 1, plen, fout ) != plen || + fwrite( s, 1, slen, fout ) != slen ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + } + else + printf( "%s%s", p, s ); + +cleanup: + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * Import X from unsigned binary data, big endian + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t i, j, n; + + for( n = 0; n < buflen; n++ ) + if( buf[n] != 0 ) + break; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = buflen, j = 0; i > n; i--, j++ ) + X->p[j / ciL] |= ((t_uint) buf[i - 1]) << ((j % ciL) << 3); + +cleanup: + + return( ret ); +} + +/* + * Export X into unsigned binary data, big endian + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ) +{ + size_t i, j, n; + + n = mpi_size( X ); + + if( buflen < n ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + memset( buf, 0, buflen ); + + for( i = buflen - 1, j = 0; n > 0; i--, j++, n-- ) + buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) ); + + return( 0 ); +} + +/* + * Left-shift: X <<= count + */ +int mpi_shift_l( mpi *X, size_t count ) +{ + int ret; + size_t i, v0, t1; + t_uint r0 = 0, r1; + + v0 = count / (biL ); + t1 = count & (biL - 1); + + i = mpi_msb( X ) + count; + + if( X->n * biL < i ) + MPI_CHK( mpi_grow( X, BITS_TO_LIMBS( i ) ) ); + + ret = 0; + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = X->n; i > v0; i-- ) + X->p[i - 1] = X->p[i - v0 - 1]; + + for( ; i > 0; i-- ) + X->p[i - 1] = 0; + } + + /* + * shift by count % limb_size + */ + if( t1 > 0 ) + { + for( i = v0; i < X->n; i++ ) + { + r1 = X->p[i] >> (biL - t1); + X->p[i] <<= t1; + X->p[i] |= r0; + r0 = r1; + } + } + +cleanup: + + return( ret ); +} + +/* + * Right-shift: X >>= count + */ +int mpi_shift_r( mpi *X, size_t count ) +{ + size_t i, v0, v1; + t_uint r0 = 0, r1; + + v0 = count / biL; + v1 = count & (biL - 1); + + if( v0 > X->n || ( v0 == X->n && v1 > 0 ) ) + return mpi_lset( X, 0 ); + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = 0; i < X->n - v0; i++ ) + X->p[i] = X->p[i + v0]; + + for( ; i < X->n; i++ ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if( v1 > 0 ) + { + for( i = X->n; i > 0; i-- ) + { + r1 = X->p[i - 1] << (biL - v1); + X->p[i - 1] >>= v1; + X->p[i - 1] |= r0; + r0 = r1; + } + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( 1 ); + if( j > i ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( 1 ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -1 ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( X->s ); + if( j > i ) return( -Y->s ); + + if( X->s > 0 && Y->s < 0 ) return( 1 ); + if( Y->s > 0 && X->s < 0 ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( X->s ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -X->s ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_int( const mpi *X, t_sint z ) +{ + mpi Y; + t_uint p[1]; + + *p = ( z < 0 ) ? -z : z; + Y.s = ( z < 0 ) ? -1 : 1; + Y.n = 1; + Y.p = p; + + return( mpi_cmp_mpi( X, &Y ) ); +} + +/* + * Unsigned addition: X = |A| + |B| (HAC 14.7) + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j; + t_uint *o, *p, c; + + if( X == B ) + { + const mpi *T = A; A = X; B = T; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned additions. + */ + X->s = 1; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, j ) ); + + o = B->p; p = X->p; c = 0; + + for( i = 0; i < j; i++, o++, p++ ) + { + *p += c; c = ( *p < c ); + *p += *o; c += ( *p < *o ); + } + + while( c != 0 ) + { + if( i >= X->n ) + { + MPI_CHK( mpi_grow( X, i + 1 ) ); + p = X->p + i; + } + + *p += c; c = ( *p < c ); i++; p++; + } + +cleanup: + + return( ret ); +} + +/* + * Helper for mpi substraction + */ +static void mpi_sub_hlp( size_t n, t_uint *s, t_uint *d ) +{ + size_t i; + t_uint c, z; + + for( i = c = 0; i < n; i++, s++, d++ ) + { + z = ( *d < c ); *d -= c; + c = ( *d < *s ) + z; *d -= *s; + } + + while( c != 0 ) + { + z = ( *d < c ); *d -= c; + c = z; i++; d++; + } +} + +/* + * Unsigned substraction: X = |A| - |B| (HAC 14.9) + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ) +{ + mpi TB; + int ret; + size_t n; + + if( mpi_cmp_abs( A, B ) < 0 ) + return( POLARSSL_ERR_MPI_NEGATIVE_VALUE ); + + mpi_init( &TB ); + + if( X == B ) + { + MPI_CHK( mpi_copy( &TB, B ) ); + B = &TB; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned substractions. + */ + X->s = 1; + + ret = 0; + + for( n = B->n; n > 0; n-- ) + if( B->p[n - 1] != 0 ) + break; + + mpi_sub_hlp( n, B->p, X->p ); + +cleanup: + + mpi_free( &TB ); + + return( ret ); +} + +/* + * Signed addition: X = A + B + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s < 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed substraction: X = A - B + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s > 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed addition: X = A + b + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_add_mpi( X, A, &_B ) ); +} + +/* + * Signed substraction: X = A - b + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_sub_mpi( X, A, &_B ) ); +} + +/* + * Helper for mpi multiplication + */ +static +#if defined(__APPLE__) && defined(__arm__) +/* + * Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn) + * appears to need this to prevent bad ARM code generation at -O3. + */ +__attribute__ ((noinline)) +#endif +void mpi_mul_hlp( size_t i, t_uint *s, t_uint *d, t_uint b ) +{ + t_uint c = 0, t = 0; + +#if defined(MULADDC_HUIT) + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_HUIT + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#else + for( ; i >= 16; i -= 16 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#endif + + t++; + + do { + *d += c; c = ( *d < c ); d++; + } + while( c != 0 ); +} + +/* + * Baseline multiplication: X = A * B (HAC 14.12) + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j; + mpi TA, TB; + + mpi_init( &TA ); mpi_init( &TB ); + + if( X == A ) { MPI_CHK( mpi_copy( &TA, A ) ); A = &TA; } + if( X == B ) { MPI_CHK( mpi_copy( &TB, B ) ); B = &TB; } + + for( i = A->n; i > 0; i-- ) + if( A->p[i - 1] != 0 ) + break; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, i + j ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i++; j > 0; j-- ) + mpi_mul_hlp( i - 1, A->p, X->p + j - 1, B->p[j - 1] ); + + X->s = A->s * B->s; + +cleanup: + + mpi_free( &TB ); mpi_free( &TA ); + + return( ret ); +} + +/* + * Baseline multiplication: X = A * b + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + _B.s = 1; + _B.n = 1; + _B.p = p; + p[0] = b; + + return( mpi_mul_mpi( X, A, &_B ) ); +} + +/* + * Division by mpi: A = Q * B + R (HAC 14.20) + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, n, t, k; + mpi X, Y, Z, T1, T2; + + if( mpi_cmp_int( B, 0 ) == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z ); + mpi_init( &T1 ); mpi_init( &T2 ); + + if( mpi_cmp_abs( A, B ) < 0 ) + { + if( Q != NULL ) MPI_CHK( mpi_lset( Q, 0 ) ); + if( R != NULL ) MPI_CHK( mpi_copy( R, A ) ); + return( 0 ); + } + + MPI_CHK( mpi_copy( &X, A ) ); + MPI_CHK( mpi_copy( &Y, B ) ); + X.s = Y.s = 1; + + MPI_CHK( mpi_grow( &Z, A->n + 2 ) ); + MPI_CHK( mpi_lset( &Z, 0 ) ); + MPI_CHK( mpi_grow( &T1, 2 ) ); + MPI_CHK( mpi_grow( &T2, 3 ) ); + + k = mpi_msb( &Y ) % biL; + if( k < biL - 1 ) + { + k = biL - 1 - k; + MPI_CHK( mpi_shift_l( &X, k ) ); + MPI_CHK( mpi_shift_l( &Y, k ) ); + } + else k = 0; + + n = X.n - 1; + t = Y.n - 1; + MPI_CHK( mpi_shift_l( &Y, biL * (n - t) ) ); + + while( mpi_cmp_mpi( &X, &Y ) >= 0 ) + { + Z.p[n - t]++; + mpi_sub_mpi( &X, &X, &Y ); + } + mpi_shift_r( &Y, biL * (n - t) ); + + for( i = n; i > t ; i-- ) + { + if( X.p[i] >= Y.p[t] ) + Z.p[i - t - 1] = ~0; + else + { +#if defined(POLARSSL_HAVE_UDBL) + t_udbl r; + + r = (t_udbl) X.p[i] << biL; + r |= (t_udbl) X.p[i - 1]; + r /= Y.p[t]; + if( r > ((t_udbl) 1 << biL) - 1) + r = ((t_udbl) 1 << biL) - 1; + + Z.p[i - t - 1] = (t_uint) r; +#else + /* + * __udiv_qrnnd_c, from gmp/longlong.h + */ + t_uint q0, q1, r0, r1; + t_uint d0, d1, d, m; + + d = Y.p[t]; + d0 = ( d << biH ) >> biH; + d1 = ( d >> biH ); + + q1 = X.p[i] / d1; + r1 = X.p[i] - d1 * q1; + r1 <<= biH; + r1 |= ( X.p[i - 1] >> biH ); + + m = q1 * d0; + if( r1 < m ) + { + q1--, r1 += d; + while( r1 >= d && r1 < m ) + q1--, r1 += d; + } + r1 -= m; + + q0 = r1 / d1; + r0 = r1 - d1 * q0; + r0 <<= biH; + r0 |= ( X.p[i - 1] << biH ) >> biH; + + m = q0 * d0; + if( r0 < m ) + { + q0--, r0 += d; + while( r0 >= d && r0 < m ) + q0--, r0 += d; + } + r0 -= m; + + Z.p[i - t - 1] = ( q1 << biH ) | q0; +#endif + } + + Z.p[i - t - 1]++; + do + { + Z.p[i - t - 1]--; + + MPI_CHK( mpi_lset( &T1, 0 ) ); + T1.p[0] = (t < 1) ? 0 : Y.p[t - 1]; + T1.p[1] = Y.p[t]; + MPI_CHK( mpi_mul_int( &T1, &T1, Z.p[i - t - 1] ) ); + + MPI_CHK( mpi_lset( &T2, 0 ) ); + T2.p[0] = (i < 2) ? 0 : X.p[i - 2]; + T2.p[1] = (i < 1) ? 0 : X.p[i - 1]; + T2.p[2] = X.p[i]; + } + while( mpi_cmp_mpi( &T1, &T2 ) > 0 ); + + MPI_CHK( mpi_mul_int( &T1, &Y, Z.p[i - t - 1] ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_sub_mpi( &X, &X, &T1 ) ); + + if( mpi_cmp_int( &X, 0 ) < 0 ) + { + MPI_CHK( mpi_copy( &T1, &Y ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_add_mpi( &X, &X, &T1 ) ); + Z.p[i - t - 1]--; + } + } + + if( Q != NULL ) + { + mpi_copy( Q, &Z ); + Q->s = A->s * B->s; + } + + if( R != NULL ) + { + mpi_shift_r( &X, k ); + X.s = A->s; + mpi_copy( R, &X ); + + if( mpi_cmp_int( R, 0 ) == 0 ) + R->s = 1; + } + +cleanup: + + mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z ); + mpi_free( &T1 ); mpi_free( &T2 ); + + return( ret ); +} + +/* + * Division by int: A = Q * b + R + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_div_mpi( Q, R, A, &_B ) ); +} + +/* + * Modulo: R = A mod B + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + + if( mpi_cmp_int( B, 0 ) < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + MPI_CHK( mpi_div_mpi( NULL, R, A, B ) ); + + while( mpi_cmp_int( R, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( R, R, B ) ); + + while( mpi_cmp_mpi( R, B ) >= 0 ) + MPI_CHK( mpi_sub_mpi( R, R, B ) ); + +cleanup: + + return( ret ); +} + +/* + * Modulo: r = A mod b + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ) +{ + size_t i; + t_uint x, y, z; + + if( b == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + if( b < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + /* + * handle trivial cases + */ + if( b == 1 ) + { + *r = 0; + return( 0 ); + } + + if( b == 2 ) + { + *r = A->p[0] & 1; + return( 0 ); + } + + /* + * general case + */ + for( i = A->n, y = 0; i > 0; i-- ) + { + x = A->p[i - 1]; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + + x <<= biH; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + } + + /* + * If A is negative, then the current y represents a negative value. + * Flipping it to the positive side. + */ + if( A->s < 0 && y != 0 ) + y = b - y; + + *r = y; + + return( 0 ); +} + +/* + * Fast Montgomery initialization (thanks to Tom St Denis) + */ +static void mpi_montg_init( t_uint *mm, const mpi *N ) +{ + t_uint x, m0 = N->p[0]; + + x = m0; + x += ( ( m0 + 2 ) & 4 ) << 1; + x *= ( 2 - ( m0 * x ) ); + + if( biL >= 16 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 32 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 64 ) x *= ( 2 - ( m0 * x ) ); + + *mm = ~x + 1; +} + +/* + * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) + */ +static void mpi_montmul( mpi *A, const mpi *B, const mpi *N, t_uint mm, const mpi *T ) +{ + size_t i, n, m; + t_uint u0, u1, *d; + + memset( T->p, 0, T->n * ciL ); + + d = T->p; + n = N->n; + m = ( B->n < n ) ? B->n : n; + + for( i = 0; i < n; i++ ) + { + /* + * T = (T + u0*B + u1*N) / 2^biL + */ + u0 = A->p[i]; + u1 = ( d[0] + u0 * B->p[0] ) * mm; + + mpi_mul_hlp( m, B->p, d, u0 ); + mpi_mul_hlp( n, N->p, d, u1 ); + + *d++ = u0; d[n + 1] = 0; + } + + memcpy( A->p, d, (n + 1) * ciL ); + + if( mpi_cmp_abs( A, N ) >= 0 ) + mpi_sub_hlp( n, N->p, A->p ); + else + /* prevent timing attacks */ + mpi_sub_hlp( n, A->p, T->p ); +} + +/* + * Montgomery reduction: A = A * R^-1 mod N + */ +static void mpi_montred( mpi *A, const mpi *N, t_uint mm, const mpi *T ) +{ + t_uint z = 1; + mpi U; + + U.n = U.s = (int) z; + U.p = &z; + + mpi_montmul( A, &U, N, mm, T ); +} + +/* + * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ) +{ + int ret; + size_t wbits, wsize, one = 1; + size_t i, j, nblimbs; + size_t bufsize, nbits; + t_uint ei, mm, state; + mpi RR, T, W[ 2 << POLARSSL_MPI_WINDOW_SIZE ], Apos; + int neg; + + if( mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + if( mpi_cmp_int( E, 0 ) < 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + /* + * Init temps and window size + */ + mpi_montg_init( &mm, N ); + mpi_init( &RR ); mpi_init( &T ); + memset( W, 0, sizeof( W ) ); + + i = mpi_msb( E ); + + wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : + ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + + if( wsize > POLARSSL_MPI_WINDOW_SIZE ) + wsize = POLARSSL_MPI_WINDOW_SIZE; + + j = N->n + 1; + MPI_CHK( mpi_grow( X, j ) ); + MPI_CHK( mpi_grow( &W[1], j ) ); + MPI_CHK( mpi_grow( &T, j * 2 ) ); + + /* + * Compensate for negative A (and correct at the end) + */ + neg = ( A->s == -1 ); + + mpi_init( &Apos ); + if( neg ) + { + MPI_CHK( mpi_copy( &Apos, A ) ); + Apos.s = 1; + A = &Apos; + } + + /* + * If 1st call, pre-compute R^2 mod N + */ + if( _RR == NULL || _RR->p == NULL ) + { + MPI_CHK( mpi_lset( &RR, 1 ) ); + MPI_CHK( mpi_shift_l( &RR, N->n * 2 * biL ) ); + MPI_CHK( mpi_mod_mpi( &RR, &RR, N ) ); + + if( _RR != NULL ) + memcpy( _RR, &RR, sizeof( mpi ) ); + } + else + memcpy( &RR, _RR, sizeof( mpi ) ); + + /* + * W[1] = A * R^2 * R^-1 mod N = A * R mod N + */ + if( mpi_cmp_mpi( A, N ) >= 0 ) + mpi_mod_mpi( &W[1], A, N ); + else mpi_copy( &W[1], A ); + + mpi_montmul( &W[1], &RR, N, mm, &T ); + + /* + * X = R^2 * R^-1 mod N = R mod N + */ + MPI_CHK( mpi_copy( X, &RR ) ); + mpi_montred( X, N, mm, &T ); + + if( wsize > 1 ) + { + /* + * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) + */ + j = one << (wsize - 1); + + MPI_CHK( mpi_grow( &W[j], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[j], &W[1] ) ); + + for( i = 0; i < wsize - 1; i++ ) + mpi_montmul( &W[j], &W[j], N, mm, &T ); + + /* + * W[i] = W[i - 1] * W[1] + */ + for( i = j + 1; i < (one << wsize); i++ ) + { + MPI_CHK( mpi_grow( &W[i], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[i], &W[i - 1] ) ); + + mpi_montmul( &W[i], &W[1], N, mm, &T ); + } + } + + nblimbs = E->n; + bufsize = 0; + nbits = 0; + wbits = 0; + state = 0; + + while( 1 ) + { + if( bufsize == 0 ) + { + if( nblimbs-- == 0 ) + break; + + bufsize = sizeof( t_uint ) << 3; + } + + bufsize--; + + ei = (E->p[nblimbs] >> bufsize) & 1; + + /* + * skip leading 0s + */ + if( ei == 0 && state == 0 ) + continue; + + if( ei == 0 && state == 1 ) + { + /* + * out of window, square X + */ + mpi_montmul( X, X, N, mm, &T ); + continue; + } + + /* + * add ei to current window + */ + state = 2; + + nbits++; + wbits |= (ei << (wsize - nbits)); + + if( nbits == wsize ) + { + /* + * X = X^wsize R^-1 mod N + */ + for( i = 0; i < wsize; i++ ) + mpi_montmul( X, X, N, mm, &T ); + + /* + * X = X * W[wbits] R^-1 mod N + */ + mpi_montmul( X, &W[wbits], N, mm, &T ); + + state--; + nbits = 0; + wbits = 0; + } + } + + /* + * process the remaining bits + */ + for( i = 0; i < nbits; i++ ) + { + mpi_montmul( X, X, N, mm, &T ); + + wbits <<= 1; + + if( (wbits & (one << wsize)) != 0 ) + mpi_montmul( X, &W[1], N, mm, &T ); + } + + /* + * X = A^E * R * R^-1 mod N = A^E mod N + */ + mpi_montred( X, N, mm, &T ); + + if( neg ) + { + X->s = -1; + mpi_add_mpi( X, N, X ); + } + +cleanup: + + for( i = (one << (wsize - 1)); i < (one << wsize); i++ ) + mpi_free( &W[i] ); + + mpi_free( &W[1] ); mpi_free( &T ); mpi_free( &Apos ); + + if( _RR == NULL ) + mpi_free( &RR ); + + return( ret ); +} + +/* + * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ) +{ + int ret; + size_t lz, lzt; + mpi TG, TA, TB; + + mpi_init( &TG ); mpi_init( &TA ); mpi_init( &TB ); + + MPI_CHK( mpi_copy( &TA, A ) ); + MPI_CHK( mpi_copy( &TB, B ) ); + + lz = mpi_lsb( &TA ); + lzt = mpi_lsb( &TB ); + + if ( lzt < lz ) + lz = lzt; + + MPI_CHK( mpi_shift_r( &TA, lz ) ); + MPI_CHK( mpi_shift_r( &TB, lz ) ); + + TA.s = TB.s = 1; + + while( mpi_cmp_int( &TA, 0 ) != 0 ) + { + MPI_CHK( mpi_shift_r( &TA, mpi_lsb( &TA ) ) ); + MPI_CHK( mpi_shift_r( &TB, mpi_lsb( &TB ) ) ); + + if( mpi_cmp_mpi( &TA, &TB ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( &TA, &TA, &TB ) ); + MPI_CHK( mpi_shift_r( &TA, 1 ) ); + } + else + { + MPI_CHK( mpi_sub_abs( &TB, &TB, &TA ) ); + MPI_CHK( mpi_shift_r( &TB, 1 ) ); + } + } + + MPI_CHK( mpi_shift_l( &TB, lz ) ); + MPI_CHK( mpi_copy( G, &TB ) ); + +cleanup: + + mpi_free( &TG ); mpi_free( &TA ); mpi_free( &TB ); + + return( ret ); +} + +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( size ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + MPI_CHK( f_rng( p_rng, (unsigned char *) X->p, size ) ); + +cleanup: + return( ret ); +} + +/* + * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ) +{ + int ret; + mpi G, TA, TU, U1, U2, TB, TV, V1, V2; + + if( mpi_cmp_int( N, 0 ) <= 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &TA ); mpi_init( &TU ); mpi_init( &U1 ); mpi_init( &U2 ); + mpi_init( &G ); mpi_init( &TB ); mpi_init( &TV ); + mpi_init( &V1 ); mpi_init( &V2 ); + + MPI_CHK( mpi_gcd( &G, A, N ) ); + + if( mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + MPI_CHK( mpi_mod_mpi( &TA, A, N ) ); + MPI_CHK( mpi_copy( &TU, &TA ) ); + MPI_CHK( mpi_copy( &TB, N ) ); + MPI_CHK( mpi_copy( &TV, N ) ); + + MPI_CHK( mpi_lset( &U1, 1 ) ); + MPI_CHK( mpi_lset( &U2, 0 ) ); + MPI_CHK( mpi_lset( &V1, 0 ) ); + MPI_CHK( mpi_lset( &V2, 1 ) ); + + do + { + while( ( TU.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TU, 1 ) ); + + if( ( U1.p[0] & 1 ) != 0 || ( U2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &U1, &U1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &U1, 1 ) ); + MPI_CHK( mpi_shift_r( &U2, 1 ) ); + } + + while( ( TV.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TV, 1 ) ); + + if( ( V1.p[0] & 1 ) != 0 || ( V2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &V1, &V1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &V1, 1 ) ); + MPI_CHK( mpi_shift_r( &V2, 1 ) ); + } + + if( mpi_cmp_mpi( &TU, &TV ) >= 0 ) + { + MPI_CHK( mpi_sub_mpi( &TU, &TU, &TV ) ); + MPI_CHK( mpi_sub_mpi( &U1, &U1, &V1 ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &V2 ) ); + } + else + { + MPI_CHK( mpi_sub_mpi( &TV, &TV, &TU ) ); + MPI_CHK( mpi_sub_mpi( &V1, &V1, &U1 ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &U2 ) ); + } + } + while( mpi_cmp_int( &TU, 0 ) != 0 ); + + while( mpi_cmp_int( &V1, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( &V1, &V1, N ) ); + + while( mpi_cmp_mpi( &V1, N ) >= 0 ) + MPI_CHK( mpi_sub_mpi( &V1, &V1, N ) ); + + MPI_CHK( mpi_copy( X, &V1 ) ); + +cleanup: + + mpi_free( &TA ); mpi_free( &TU ); mpi_free( &U1 ); mpi_free( &U2 ); + mpi_free( &G ); mpi_free( &TB ); mpi_free( &TV ); + mpi_free( &V1 ); mpi_free( &V2 ); + + return( ret ); +} + +#if defined(POLARSSL_GENPRIME) + +static const int small_prime[] = +{ + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, + 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, + 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997, -103 +}; + +/* + * Miller-Rabin primality test (HAC 4.24) + */ +int mpi_is_prime( mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret, xs; + size_t i, j, n, s; + mpi W, R, T, A, RR; + + if( mpi_cmp_int( X, 0 ) == 0 || + mpi_cmp_int( X, 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + if( mpi_cmp_int( X, 2 ) == 0 ) + return( 0 ); + + mpi_init( &W ); mpi_init( &R ); mpi_init( &T ); mpi_init( &A ); + mpi_init( &RR ); + + xs = X->s; X->s = 1; + + /* + * test trivial factors first + */ + if( ( X->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + for( i = 0; small_prime[i] > 0; i++ ) + { + t_uint r; + + if( mpi_cmp_int( X, small_prime[i] ) <= 0 ) + return( 0 ); + + MPI_CHK( mpi_mod_int( &r, X, small_prime[i] ) ); + + if( r == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + } + + /* + * W = |X| - 1 + * R = W >> lsb( W ) + */ + MPI_CHK( mpi_sub_int( &W, X, 1 ) ); + s = mpi_lsb( &W ); + MPI_CHK( mpi_copy( &R, &W ) ); + MPI_CHK( mpi_shift_r( &R, s ) ); + + i = mpi_msb( X ); + /* + * HAC, table 4.4 + */ + n = ( ( i >= 1300 ) ? 2 : ( i >= 850 ) ? 3 : + ( i >= 650 ) ? 4 : ( i >= 350 ) ? 8 : + ( i >= 250 ) ? 12 : ( i >= 150 ) ? 18 : 27 ); + + for( i = 0; i < n; i++ ) + { + /* + * pick a random A, 1 < A < |X| - 1 + */ + MPI_CHK( mpi_fill_random( &A, X->n * ciL, f_rng, p_rng ) ); + + if( mpi_cmp_mpi( &A, &W ) >= 0 ) + { + j = mpi_msb( &A ) - mpi_msb( &W ); + MPI_CHK( mpi_shift_r( &A, j + 1 ) ); + } + A.p[0] |= 3; + + /* + * A = A^R mod |X| + */ + MPI_CHK( mpi_exp_mod( &A, &A, &R, X, &RR ) ); + + if( mpi_cmp_mpi( &A, &W ) == 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + continue; + + j = 1; + while( j < s && mpi_cmp_mpi( &A, &W ) != 0 ) + { + /* + * A = A * A mod |X| + */ + MPI_CHK( mpi_mul_mpi( &T, &A, &A ) ); + MPI_CHK( mpi_mod_mpi( &A, &T, X ) ); + + if( mpi_cmp_int( &A, 1 ) == 0 ) + break; + + j++; + } + + /* + * not prime if A != |X| - 1 or A == 1 + */ + if( mpi_cmp_mpi( &A, &W ) != 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + break; + } + } + +cleanup: + + X->s = xs; + + mpi_free( &W ); mpi_free( &R ); mpi_free( &T ); mpi_free( &A ); + mpi_free( &RR ); + + return( ret ); +} + +/* + * Prime number generation + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + size_t k, n; + mpi Y; + + if( nbits < 3 || nbits > POLARSSL_MPI_MAX_BITS ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &Y ); + + n = BITS_TO_LIMBS( nbits ); + + MPI_CHK( mpi_fill_random( X, n * ciL, f_rng, p_rng ) ); + + k = mpi_msb( X ); + if( k < nbits ) MPI_CHK( mpi_shift_l( X, nbits - k ) ); + if( k > nbits ) MPI_CHK( mpi_shift_r( X, k - nbits ) ); + + X->p[0] |= 3; + + if( dh_flag == 0 ) + { + while( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) != 0 ) + { + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MPI_CHK( mpi_add_int( X, X, 2 ) ); + } + } + else + { + MPI_CHK( mpi_sub_int( &Y, X, 1 ) ); + MPI_CHK( mpi_shift_r( &Y, 1 ) ); + + while( 1 ) + { + if( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) == 0 ) + { + if( ( ret = mpi_is_prime( &Y, f_rng, p_rng ) ) == 0 ) + break; + + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + } + + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MPI_CHK( mpi_add_int( &Y, X, 1 ) ); + MPI_CHK( mpi_add_int( X, X, 2 ) ); + MPI_CHK( mpi_shift_r( &Y, 1 ) ); + } + } + +cleanup: + + mpi_free( &Y ); + + return( ret ); +} + +#endif + +#if defined(POLARSSL_SELF_TEST) + +#define GCD_PAIR_COUNT 3 + +static const int gcd_pairs[GCD_PAIR_COUNT][3] = +{ + { 693, 609, 21 }, + { 1764, 868, 28 }, + { 768454923, 542167814, 1 } +}; + +/* + * Checkup routine + */ +int mpi_self_test( int verbose ) +{ + int ret, i; + mpi A, E, N, X, Y, U, V; + + mpi_init( &A ); mpi_init( &E ); mpi_init( &N ); mpi_init( &X ); + mpi_init( &Y ); mpi_init( &U ); mpi_init( &V ); + + MPI_CHK( mpi_read_string( &A, 16, + "EFE021C2645FD1DC586E69184AF4A31E" \ + "D5F53E93B5F123FA41680867BA110131" \ + "944FE7952E2517337780CB0DB80E61AA" \ + "E7C8DDC6C5C6AADEB34EB38A2F40D5E6" ) ); + + MPI_CHK( mpi_read_string( &E, 16, + "B2E7EFD37075B9F03FF989C7C5051C20" \ + "34D2A323810251127E7BF8625A4F49A5" \ + "F3E27F4DA8BD59C47D6DAABA4C8127BD" \ + "5B5C25763222FEFCCFC38B832366C29E" ) ); + + MPI_CHK( mpi_read_string( &N, 16, + "0066A198186C18C10B2F5ED9B522752A" \ + "9830B69916E535C8F047518A889A43A5" \ + "94B6BED27A168D31D4A52F88925AA8F5" ) ); + + MPI_CHK( mpi_mul_mpi( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "602AB7ECA597A3D6B56FF9829A5E8B85" \ + "9E857EA95A03512E2BAE7391688D264A" \ + "A5663B0341DB9CCFD2C4C5F421FEC814" \ + "8001B72E848A38CAE1C65F78E56ABDEF" \ + "E12D3C039B8A02D6BE593F0BBBDA56F1" \ + "ECF677152EF804370C1A305CAF3B5BF1" \ + "30879B56C61DE584A0F53A2447A51E" ) ); + + if( verbose != 0 ) + printf( " MPI test #1 (mul_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_div_mpi( &X, &Y, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "256567336059E52CAE22925474705F39A94" ) ); + + MPI_CHK( mpi_read_string( &V, 16, + "6613F26162223DF488E9CD48CC132C7A" \ + "0AC93C701B001B092E4E5B9F73BCD27B" \ + "9EE50D0657C77F374E903CDFA4C642" ) ); + + if( verbose != 0 ) + printf( " MPI test #2 (div_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 || + mpi_cmp_mpi( &Y, &V ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_exp_mod( &X, &A, &E, &N, NULL ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "36E139AEA55215609D2816998ED020BB" \ + "BD96C37890F65171D948E9BC7CBAA4D9" \ + "325D24D6A3C12710F10A09FA08AB87" ) ); + + if( verbose != 0 ) + printf( " MPI test #3 (exp_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + +#if defined(POLARSSL_GENPRIME) + MPI_CHK( mpi_inv_mod( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \ + "C3DBA76456363A10869622EAC2DD84EC" \ + "C5B8A74DAC4D09E03B5E0BE779F2DF61" ) ); + + if( verbose != 0 ) + printf( " MPI test #4 (inv_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); +#endif + + if( verbose != 0 ) + printf( " MPI test #5 (simple gcd): " ); + + for ( i = 0; i < GCD_PAIR_COUNT; i++) + { + MPI_CHK( mpi_lset( &X, gcd_pairs[i][0] ) ); + MPI_CHK( mpi_lset( &Y, gcd_pairs[i][1] ) ); + + MPI_CHK( mpi_gcd( &A, &X, &Y ) ); + + if( mpi_cmp_int( &A, gcd_pairs[i][2] ) != 0 ) + { + if( verbose != 0 ) + printf( "failed at %d\n", i ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + +cleanup: + + if( ret != 0 && verbose != 0 ) + printf( "Unexpected error, return code = %08X\n", ret ); + + mpi_free( &A ); mpi_free( &E ); mpi_free( &N ); mpi_free( &X ); + mpi_free( &Y ); mpi_free( &U ); mpi_free( &V ); + + if( verbose != 0 ) + printf( "\n" ); + + return( ret ); +} + +#endif + +#endif diff --git a/common/polarssl/bignum.h b/common/polarssl/bignum.h new file mode 100644 index 00000000..5eaf1a57 --- /dev/null +++ b/common/polarssl/bignum.h @@ -0,0 +1,685 @@ +/** + * \file bignum.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_BIGNUM_H +#define POLARSSL_BIGNUM_H + +#include +#include + +#include "polarssl_config.h" + +#ifdef _MSC_VER +#include +#if (_MSC_VER <= 1200) +typedef signed short int16_t; +typedef unsigned short uint16_t; +#else +typedef INT16 int16_t; +typedef UINT16 uint16_t; +#endif +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; +#else +#include +#endif + +#define POLARSSL_ERR_MPI_FILE_IO_ERROR -0x0002 /**< An error occurred while reading from or writing to a file. */ +#define POLARSSL_ERR_MPI_BAD_INPUT_DATA -0x0004 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_MPI_INVALID_CHARACTER -0x0006 /**< There is an invalid character in the digit string. */ +#define POLARSSL_ERR_MPI_BUFFER_TOO_SMALL -0x0008 /**< The buffer is too small to write to. */ +#define POLARSSL_ERR_MPI_NEGATIVE_VALUE -0x000A /**< The input arguments are negative or result in illegal output. */ +#define POLARSSL_ERR_MPI_DIVISION_BY_ZERO -0x000C /**< The input argument for division is zero, which is not allowed. */ +#define POLARSSL_ERR_MPI_NOT_ACCEPTABLE -0x000E /**< The input arguments are not acceptable. */ +#define POLARSSL_ERR_MPI_MALLOC_FAILED -0x0010 /**< Memory allocation failed. */ + +#define MPI_CHK(f) if( ( ret = f ) != 0 ) goto cleanup + +/* + * Maximum size MPIs are allowed to grow to in number of limbs. + */ +#define POLARSSL_MPI_MAX_LIMBS 10000 + +#if !defined(POLARSSL_CONFIG_OPTIONS) +/* + * Maximum window size used for modular exponentiation. Default: 6 + * Minimum value: 1. Maximum value: 6. + * + * Result is an array of ( 2 << POLARSSL_MPI_WINDOW_SIZE ) MPIs used + * for the sliding window calculation. (So 64 by default) + * + * Reduction in size, reduces speed. + */ +#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ + +/* + * Maximum size of MPIs allowed in bits and bytes for user-MPIs. + * ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits ) + * + * Note: Calculations can results temporarily in larger MPIs. So the number + * of limbs required (POLARSSL_MPI_MAX_LIMBS) is higher. + */ +#define POLARSSL_MPI_MAX_SIZE 512 /**< Maximum number of bytes for usable MPIs. */ + +#endif /* !POLARSSL_CONFIG_OPTIONS */ + +#define POLARSSL_MPI_MAX_BITS ( 8 * POLARSSL_MPI_MAX_SIZE ) /**< Maximum number of bits for usable MPIs. */ + +/* + * When reading from files with mpi_read_file() and writing to files with + * mpi_write_file() the buffer should have space + * for a (short) label, the MPI (in the provided radix), the newline + * characters and the '\0'. + * + * By default we assume at least a 10 char label, a minimum radix of 10 + * (decimal) and a maximum of 4096 bit numbers (1234 decimal chars). + * Autosized at compile time for at least a 10 char label, a minimum radix + * of 10 (decimal) for a number of POLARSSL_MPI_MAX_BITS size. + * + * This used to be statically sized to 1250 for a maximum of 4096 bit + * numbers (1234 decimal chars). + * + * Calculate using the formula: + * POLARSSL_MPI_RW_BUFFER_SIZE = ceil(POLARSSL_MPI_MAX_BITS / ln(10) * ln(2)) + + * LabelSize + 6 + */ +#define POLARSSL_MPI_MAX_BITS_SCALE100 ( 100 * POLARSSL_MPI_MAX_BITS ) +#define LN_2_DIV_LN_10_SCALE100 332 +#define POLARSSL_MPI_RW_BUFFER_SIZE ( ((POLARSSL_MPI_MAX_BITS_SCALE100 + LN_2_DIV_LN_10_SCALE100 - 1) / LN_2_DIV_LN_10_SCALE100) + 10 + 6 ) + +/* + * Define the base integer type, architecture-wise + */ +#if defined(POLARSSL_HAVE_INT8) +typedef signed char t_sint; +typedef unsigned char t_uint; +typedef uint16_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else +#if defined(POLARSSL_HAVE_INT16) +typedef int16_t t_sint; +typedef uint16_t t_uint; +typedef uint32_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else + #if ( defined(_MSC_VER) && defined(_M_AMD64) ) + typedef int64_t t_sint; + typedef uint64_t t_uint; + #else + #if ( defined(__GNUC__) && ( \ + defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) || \ + (defined(__sparc__) && defined(__arch64__)) || \ + defined(__s390x__) ) ) + typedef int64_t t_sint; + typedef uint64_t t_uint; + typedef unsigned int t_udbl __attribute__((mode(TI))); + #define POLARSSL_HAVE_UDBL + #else + typedef int32_t t_sint; + typedef uint32_t t_uint; + #if ( defined(_MSC_VER) && defined(_M_IX86) ) + typedef uint64_t t_udbl; + #define POLARSSL_HAVE_UDBL + #else + #if defined( POLARSSL_HAVE_LONGLONG ) + typedef unsigned long long t_udbl; + #define POLARSSL_HAVE_UDBL + #endif + #endif + #endif + #endif +#endif /* POLARSSL_HAVE_INT16 */ +#endif /* POLARSSL_HAVE_INT8 */ + +/** + * \brief MPI structure + */ +typedef struct +{ + int s; /*!< integer sign */ + size_t n; /*!< total # of limbs */ + t_uint *p; /*!< pointer to limbs */ +} +mpi; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize one MPI + * + * \param X One MPI to initialize. + */ +void mpi_init( mpi *X ); + +/** + * \brief Unallocate one MPI + * + * \param X One MPI to unallocate. + */ +void mpi_free( mpi *X ); + +/** + * \brief Enlarge to the specified number of limbs + * + * \param X MPI to grow + * \param nblimbs The target number of limbs + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_grow( mpi *X, size_t nblimbs ); + +/** + * \brief Copy the contents of Y into X + * + * \param X Destination MPI + * \param Y Source MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_copy( mpi *X, const mpi *Y ); + +/** + * \brief Swap the contents of X and Y + * + * \param X First MPI value + * \param Y Second MPI value + */ +void mpi_swap( mpi *X, mpi *Y ); + +/** + * \brief Set value from integer + * + * \param X MPI to set + * \param z Value to use + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_lset( mpi *X, t_sint z ); + +/** + * \brief Get a specific bit from X + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * + * \return Either a 0 or a 1 + */ +int mpi_get_bit( const mpi *X, size_t pos ); + +/** + * \brief Set a bit of X to a specific value of 0 or 1 + * + * \note Will grow X if necessary to set a bit to 1 in a not yet + * existing limb. Will not grow if bit should be set to 0 + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * \param val The value to set the bit to (0 or 1) + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if val is not 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ); + +/** + * \brief Return the number of zero-bits before the least significant + * '1' bit + * + * Note: Thus also the zero-based index of the least significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_lsb( const mpi *X ); + +/** + * \brief Return the number of bits up to and including the most + * significant '1' bit' + * + * Note: Thus also the one-based index of the most significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_msb( const mpi *X ); + +/** + * \brief Return the total size in bytes + * + * \param X MPI to use + */ +size_t mpi_size( const mpi *X ); + +/** + * \brief Import from an ASCII string + * + * \param X Destination MPI + * \param radix Input numeric base + * \param s Null-terminated string buffer + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_string( mpi *X, int radix, const char *s ); + +/** + * \brief Export into an ASCII string + * + * \param X Source MPI + * \param radix Output numeric base + * \param s String buffer + * \param slen String buffer size + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code. + * *slen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *slen = 0 to obtain the + * minimum required buffer size in *slen. + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Read X from an opened file + * + * \param X Destination MPI + * \param radix Input numeric base + * \param fin Input file handle + * + * \return 0 if successful, POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if + * the file read buffer is too small or a + * POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ); + +/** + * \brief Write X into an opened file, or stdout if fout is NULL + * + * \param p Prefix, can be NULL + * \param X Source MPI + * \param radix Output numeric base + * \param fout Output file handle (can be NULL) + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + * + * \note Set fout == NULL to print X on the console. + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief Import X from unsigned binary data, big endian + * + * \param X Destination MPI + * \param buf Input buffer + * \param buflen Input buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ); + +/** + * \brief Export X into unsigned binary data, big endian + * + * \param X Source MPI + * \param buf Output buffer + * \param buflen Output buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ); + +/** + * \brief Left-shift: X <<= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_l( mpi *X, size_t count ); + +/** + * \brief Right-shift: X >>= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_r( mpi *X, size_t count ); + +/** + * \brief Compare unsigned values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if |X| is greater than |Y|, + * -1 if |X| is lesser than |Y| or + * 0 if |X| is equal to |Y| + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if X is greater than Y, + * -1 if X is lesser than Y or + * 0 if X is equal to Y + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param z The integer value to compare to + * + * \return 1 if X is greater than z, + * -1 if X is lesser than z or + * 0 if X is equal to z + */ +int mpi_cmp_int( const mpi *X, t_sint z ); + +/** + * \brief Unsigned addition: X = |A| + |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Unsigned substraction: X = |A| - |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B is greater than A + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed substraction: X = A - B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to add + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Signed substraction: X = A - b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to subtract + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Baseline multiplication: X = A * B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Baseline multiplication: X = A * b + * Note: b is an unsigned integer type, thus + * Negative values of b are ignored. + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to multiply with + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Division by mpi: A = Q * B + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Division by int: A = Q * b + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ); + +/** + * \brief Modulo: R = A mod B + * + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B < 0 + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Modulo: r = A mod b + * + * \param r Destination t_uint + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if b < 0 + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ); + +/** + * \brief Sliding-window exponentiation: X = A^E mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param E Exponent MPI + * \param N Modular MPI + * \param _RR Speed-up MPI used for recalculations + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or even or if + * E is negative + * + * \note _RR is used to avoid re-computing R*R mod N across + * multiple calls, which speeds up things a bit. It can + * be set to NULL if the extra performance is unneeded. + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ); + +/** + * \brief Fill an MPI X with size bytes of random + * + * \param X Destination MPI + * \param size Size in bytes + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Greatest common divisor: G = gcd(A, B) + * + * \param G Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ); + +/** + * \brief Modular inverse: X = A^-1 mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param N Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or nil + POLARSSL_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ); + +/** + * \brief Miller-Rabin primality test + * + * \param X MPI to check + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_NOT_ACCEPTABLE if X is not prime + */ +int mpi_is_prime( mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Prime number generation + * + * \param X Destination MPI + * \param nbits Required size of X in bits ( 3 <= nbits <= POLARSSL_MPI_MAX_BITS ) + * \param dh_flag If 1, then (X-1)/2 will be prime too + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if nbits is < 3 + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mpi_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/common/polarssl/bn_mul.h b/common/polarssl/bn_mul.h new file mode 100644 index 00000000..1c2da136 --- /dev/null +++ b/common/polarssl/bn_mul.h @@ -0,0 +1,864 @@ +/** + * \file bn_mul.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef POLARSSL_BN_MUL_H +#define POLARSSL_BN_MUL_H + +#include "bignum.h" + +#if defined(POLARSSL_HAVE_ASM) + +#if defined(__GNUC__) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( " \ + movl %%ebx, %0; \ + movl %5, %%esi; \ + movl %6, %%edi; \ + movl %7, %%ecx; \ + movl %8, %%ebx; \ + " + +#define MULADDC_CORE \ + " \ + lodsl; \ + mull %%ebx; \ + addl %%ecx, %%eax; \ + adcl $0, %%edx; \ + addl (%%edi), %%eax; \ + adcl $0, %%edx; \ + movl %%edx, %%ecx; \ + stosl; \ + " + +#if defined(POLARSSL_HAVE_SSE2) + +#define MULADDC_HUIT \ + " \ + movd %%ecx, %%mm1; \ + movd %%ebx, %%mm0; \ + movd (%%edi), %%mm3; \ + paddq %%mm3, %%mm1; \ + movd (%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + movd 4(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + movd 8(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd 12(%%esi), %%mm7; \ + pmuludq %%mm0, %%mm7; \ + paddq %%mm2, %%mm1; \ + movd 4(%%edi), %%mm3; \ + paddq %%mm4, %%mm3; \ + movd 8(%%edi), %%mm5; \ + paddq %%mm6, %%mm5; \ + movd 12(%%edi), %%mm4; \ + paddq %%mm4, %%mm7; \ + movd %%mm1, (%%edi); \ + movd 16(%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + psrlq $32, %%mm1; \ + movd 20(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + paddq %%mm3, %%mm1; \ + movd 24(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd %%mm1, 4(%%edi); \ + psrlq $32, %%mm1; \ + movd 28(%%esi), %%mm3; \ + pmuludq %%mm0, %%mm3; \ + paddq %%mm5, %%mm1; \ + movd 16(%%edi), %%mm5; \ + paddq %%mm5, %%mm2; \ + movd %%mm1, 8(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm7, %%mm1; \ + movd 20(%%edi), %%mm5; \ + paddq %%mm5, %%mm4; \ + movd %%mm1, 12(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm2, %%mm1; \ + movd 24(%%edi), %%mm5; \ + paddq %%mm5, %%mm6; \ + movd %%mm1, 16(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm4, %%mm1; \ + movd 28(%%edi), %%mm5; \ + paddq %%mm5, %%mm3; \ + movd %%mm1, 20(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm6, %%mm1; \ + movd %%mm1, 24(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm3, %%mm1; \ + movd %%mm1, 28(%%edi); \ + addl $32, %%edi; \ + addl $32, %%esi; \ + psrlq $32, %%mm1; \ + movd %%mm1, %%ecx; \ + " + +#define MULADDC_STOP \ + " \ + emms; \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); + +#else + +#define MULADDC_STOP \ + " \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( "movq %0, %%rsi " :: "m" (s)); \ + asm( "movq %0, %%rdi " :: "m" (d)); \ + asm( "movq %0, %%rcx " :: "m" (c)); \ + asm( "movq %0, %%rbx " :: "m" (b)); \ + asm( "xorq %r8, %r8 " ); + +#define MULADDC_CORE \ + asm( "movq (%rsi),%rax " ); \ + asm( "mulq %rbx " ); \ + asm( "addq $8, %rsi " ); \ + asm( "addq %rcx, %rax " ); \ + asm( "movq %r8, %rcx " ); \ + asm( "adcq $0, %rdx " ); \ + asm( "nop " ); \ + asm( "addq %rax, (%rdi) " ); \ + asm( "adcq %rdx, %rcx " ); \ + asm( "addq $8, %rdi " ); + +#define MULADDC_STOP \ + asm( "movq %%rcx, %0 " : "=m" (c)); \ + asm( "movq %%rdi, %0 " : "=m" (d)); \ + asm( "movq %%rsi, %0 " : "=m" (s) :: \ + "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( "movl %0, %%a2 " :: "m" (s)); \ + asm( "movl %0, %%a3 " :: "m" (d)); \ + asm( "movl %0, %%d3 " :: "m" (c)); \ + asm( "movl %0, %%d2 " :: "m" (b)); \ + asm( "moveq #0, %d0 " ); + +#define MULADDC_CORE \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "moveq #0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d4, %d3 " ); + +#define MULADDC_STOP \ + asm( "movl %%d3, %0 " : "=m" (c)); \ + asm( "movl %%a3, %0 " : "=m" (d)); \ + asm( "movl %%a2, %0 " : "=m" (s) :: \ + "d0", "d1", "d2", "d3", "d4", "a2", "a3" ); + +#define MULADDC_HUIT \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d0, %d3 " ); + +#endif /* MC68000 */ + +#if defined(__powerpc__) || defined(__ppc__) +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "ld r3, %0 " :: "m" (s)); \ + asm( "ld r4, %0 " :: "m" (d)); \ + asm( "ld r5, %0 " :: "m" (c)); \ + asm( "ld r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -8 " ); \ + asm( "addi r4, r4, -8 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu r7, 8(r3) " ); \ + asm( "mulld r8, r7, r6 " ); \ + asm( "mulhdu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "ld r7, 8(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stdu r8, 8(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 8 " ); \ + asm( "addi r3, r3, 8 " ); \ + asm( "std r5, %0 " : "=m" (c)); \ + asm( "std r4, %0 " : "=m" (d)); \ + asm( "std r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "ld %%r3, %0 " :: "m" (s)); \ + asm( "ld %%r4, %0 " :: "m" (d)); \ + asm( "ld %%r5, %0 " :: "m" (c)); \ + asm( "ld %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -8 " ); \ + asm( "addi %r4, %r4, -8 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu %r7, 8(%r3) " ); \ + asm( "mulld %r8, %r7, %r6 " ); \ + asm( "mulhdu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "ld %r7, 8(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stdu %r8, 8(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 8 " ); \ + asm( "addi %r3, %r3, 8 " ); \ + asm( "std %%r5, %0 " : "=m" (c)); \ + asm( "std %%r4, %0 " : "=m" (d)); \ + asm( "std %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#else /* PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "lwz r3, %0 " :: "m" (s)); \ + asm( "lwz r4, %0 " :: "m" (d)); \ + asm( "lwz r5, %0 " :: "m" (c)); \ + asm( "lwz r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -4 " ); \ + asm( "addi r4, r4, -4 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu r7, 4(r3) " ); \ + asm( "mullw r8, r7, r6 " ); \ + asm( "mulhwu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "lwz r7, 4(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stwu r8, 4(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 4 " ); \ + asm( "addi r3, r3, 4 " ); \ + asm( "stw r5, %0 " : "=m" (c)); \ + asm( "stw r4, %0 " : "=m" (d)); \ + asm( "stw r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "lwz %%r3, %0 " :: "m" (s)); \ + asm( "lwz %%r4, %0 " :: "m" (d)); \ + asm( "lwz %%r5, %0 " :: "m" (c)); \ + asm( "lwz %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -4 " ); \ + asm( "addi %r4, %r4, -4 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu %r7, 4(%r3) " ); \ + asm( "mullw %r8, %r7, %r6 " ); \ + asm( "mulhwu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "lwz %r7, 4(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stwu %r8, 4(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 4 " ); \ + asm( "addi %r3, %r3, 4 " ); \ + asm( "stw %%r5, %0 " : "=m" (c)); \ + asm( "stw %%r4, %0 " : "=m" (d)); \ + asm( "stw %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#endif /* PPC32 */ +#endif /* PPC64 */ + +#if defined(__sparc__) && defined(__sparc64__) + +#define MULADDC_INIT \ + asm( \ + " \ + ldx %3, %%o0; \ + ldx %4, %%o1; \ + ld %5, %%o2; \ + ld %6, %%o3; \ + " + +#define MULADDC_CORE \ + " \ + ld [%%o0], %%o4; \ + inc 4, %%o0; \ + ld [%%o1], %%o5; \ + umul %%o3, %%o4, %%o4; \ + addcc %%o4, %%o2, %%o4; \ + rd %%y, %%g1; \ + addx %%g1, 0, %%g1; \ + addcc %%o4, %%o5, %%o4; \ + st %%o4, [%%o1]; \ + addx %%g1, 0, %%o2; \ + inc 4, %%o1; \ + " + +#define MULADDC_STOP \ + " \ + st %%o2, %0; \ + stx %%o1, %1; \ + stx %%o0, %2; \ + " \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); +#endif /* SPARCv9 */ + +#if defined(__sparc__) && !defined(__sparc64__) + +#define MULADDC_INIT \ + asm( \ + " \ + ld %3, %%o0; \ + ld %4, %%o1; \ + ld %5, %%o2; \ + ld %6, %%o3; \ + " + +#define MULADDC_CORE \ + " \ + ld [%%o0], %%o4; \ + inc 4, %%o0; \ + ld [%%o1], %%o5; \ + umul %%o3, %%o4, %%o4; \ + addcc %%o4, %%o2, %%o4; \ + rd %%y, %%g1; \ + addx %%g1, 0, %%g1; \ + addcc %%o4, %%o5, %%o4; \ + st %%o4, [%%o1]; \ + addx %%g1, 0, %%o2; \ + inc 4, %%o1; \ + " + +#define MULADDC_STOP \ + " \ + st %%o2, %0; \ + st %%o1, %1; \ + st %%o0, %2; \ + " \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); + +#endif /* SPARCv8 */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( "lwi r3, %0 " :: "m" (s)); \ + asm( "lwi r4, %0 " :: "m" (d)); \ + asm( "lwi r5, %0 " :: "m" (c)); \ + asm( "lwi r6, %0 " :: "m" (b)); \ + asm( "andi r7, r6, 0xffff" ); \ + asm( "bsrli r6, r6, 16 " ); + +#define MULADDC_CORE \ + asm( "lhui r8, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "lhui r9, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "mul r10, r9, r6 " ); \ + asm( "mul r11, r8, r7 " ); \ + asm( "mul r12, r9, r7 " ); \ + asm( "mul r13, r8, r6 " ); \ + asm( "bsrli r8, r10, 16 " ); \ + asm( "bsrli r9, r11, 16 " ); \ + asm( "add r13, r13, r8 " ); \ + asm( "add r13, r13, r9 " ); \ + asm( "bslli r10, r10, 16 " ); \ + asm( "bslli r11, r11, 16 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r11 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "lwi r10, r4, 0 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r5 " ); \ + asm( "addc r5, r13, r0 " ); \ + asm( "swi r12, r4, 0 " ); \ + asm( "addi r4, r4, 4 " ); + +#define MULADDC_STOP \ + asm( "swi r5, %0 " : "=m" (c)); \ + asm( "swi r4, %0 " : "=m" (d)); \ + asm( "swi r3, %0 " : "=m" (s) :: \ + "r3", "r4" , "r5" , "r6" , "r7" , "r8" , \ + "r9", "r10", "r11", "r12", "r13" ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( "ld.a %%a2, %0 " :: "m" (s)); \ + asm( "ld.a %%a3, %0 " :: "m" (d)); \ + asm( "ld.w %%d4, %0 " :: "m" (c)); \ + asm( "ld.w %%d1, %0 " :: "m" (b)); \ + asm( "xor %d5, %d5 " ); + +#define MULADDC_CORE \ + asm( "ld.w %d0, [%a2+] " ); \ + asm( "madd.u %e2, %e4, %d0, %d1 " ); \ + asm( "ld.w %d0, [%a3] " ); \ + asm( "addx %d2, %d2, %d0 " ); \ + asm( "addc %d3, %d3, 0 " ); \ + asm( "mov %d4, %d3 " ); \ + asm( "st.w [%a3+], %d2 " ); + +#define MULADDC_STOP \ + asm( "st.w %0, %%d4 " : "=m" (c)); \ + asm( "st.a %0, %%a3 " : "=m" (d)); \ + asm( "st.a %0, %%a2 " : "=m" (s) :: \ + "d0", "d1", "e2", "d4", "a2", "a3" ); + +#endif /* TriCore */ + +#if defined(__arm__) + +#if defined(__thumb__) && !defined(__thumb2__) + +#define MULADDC_INIT \ + asm( \ + " \ + ldr r0, %3; \ + ldr r1, %4; \ + ldr r2, %5; \ + ldr r3, %6; \ + lsr r7, r3, #16; \ + mov r9, r7; \ + lsl r7, r3, #16; \ + lsr r7, r7, #16; \ + mov r8, r7; \ + " + +#define MULADDC_CORE \ + " \ + ldmia r0!, {r6}; \ + lsr r7, r6, #16; \ + lsl r6, r6, #16; \ + lsr r6, r6, #16; \ + mov r4, r8; \ + mul r4, r6; \ + mov r3, r9; \ + mul r6, r3; \ + mov r5, r9; \ + mul r5, r7; \ + mov r3, r8; \ + mul r7, r3; \ + lsr r3, r6, #16; \ + add r5, r5, r3; \ + lsr r3, r7, #16; \ + add r5, r5, r3; \ + add r4, r4, r2; \ + mov r2, #0; \ + adc r5, r2; \ + lsl r3, r6, #16; \ + add r4, r4, r3; \ + adc r5, r2; \ + lsl r3, r7, #16; \ + add r4, r4, r3; \ + adc r5, r2; \ + ldr r3, [r1]; \ + add r4, r4, r3; \ + adc r2, r5; \ + stmia r1!, {r4}; \ + " + +#define MULADDC_STOP \ + " \ + str r2, %0; \ + str r1, %1; \ + str r0, %2; \ + " \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "r8", "r9", "cc" \ + ); + +#else + +#define MULADDC_INIT \ + asm( \ + " \ + ldr r0, %3; \ + ldr r1, %4; \ + ldr r2, %5; \ + ldr r3, %6; \ + " + +#define MULADDC_CORE \ + " \ + ldr r4, [r0], #4; \ + mov r5, #0; \ + ldr r6, [r1]; \ + umlal r2, r5, r3, r4; \ + adds r7, r6, r2; \ + adc r2, r5, #0; \ + str r7, [r1], #4; \ + " + +#define MULADDC_STOP \ + " \ + str r2, %0; \ + str r1, %1; \ + str r0, %2; \ + " \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "cc" \ + ); + +#endif /* Thumb */ + +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( "ldq $1, %0 " :: "m" (s)); \ + asm( "ldq $2, %0 " :: "m" (d)); \ + asm( "ldq $3, %0 " :: "m" (c)); \ + asm( "ldq $4, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldq $6, 0($1) " ); \ + asm( "addq $1, 8, $1 " ); \ + asm( "mulq $6, $4, $7 " ); \ + asm( "umulh $6, $4, $6 " ); \ + asm( "addq $7, $3, $7 " ); \ + asm( "cmpult $7, $3, $3 " ); \ + asm( "ldq $5, 0($2) " ); \ + asm( "addq $7, $5, $7 " ); \ + asm( "cmpult $7, $5, $5 " ); \ + asm( "stq $7, 0($2) " ); \ + asm( "addq $2, 8, $2 " ); \ + asm( "addq $6, $3, $3 " ); \ + asm( "addq $5, $3, $3 " ); + +#define MULADDC_STOP \ + asm( "stq $3, %0 " : "=m" (c)); \ + asm( "stq $2, %0 " : "=m" (d)); \ + asm( "stq $1, %0 " : "=m" (s) :: \ + "$1", "$2", "$3", "$4", "$5", "$6", "$7" ); + +#endif /* Alpha */ + +#if defined(__mips__) + +#define MULADDC_INIT \ + asm( "lw $10, %0 " :: "m" (s)); \ + asm( "lw $11, %0 " :: "m" (d)); \ + asm( "lw $12, %0 " :: "m" (c)); \ + asm( "lw $13, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "lw $14, 0($10) " ); \ + asm( "multu $13, $14 " ); \ + asm( "addi $10, $10, 4 " ); \ + asm( "mflo $14 " ); \ + asm( "mfhi $9 " ); \ + asm( "addu $14, $12, $14 " ); \ + asm( "lw $15, 0($11) " ); \ + asm( "sltu $12, $14, $12 " ); \ + asm( "addu $15, $14, $15 " ); \ + asm( "sltu $14, $15, $14 " ); \ + asm( "addu $12, $12, $9 " ); \ + asm( "sw $15, 0($11) " ); \ + asm( "addu $12, $12, $14 " ); \ + asm( "addi $11, $11, 4 " ); + +#define MULADDC_STOP \ + asm( "sw $12, %0 " : "=m" (c)); \ + asm( "sw $11, %0 " : "=m" (d)); \ + asm( "sw $10, %0 " : "=m" (s) :: \ + "$9", "$10", "$11", "$12", "$13", "$14", "$15" ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(POLARSSL_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* POLARSSL_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(POLARSSL_HAVE_UDBL) + +#define MULADDC_INIT \ +{ \ + t_udbl r; \ + t_uint r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (t_udbl) b; \ + r0 = r; \ + r1 = r >> biL; \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + t_uint s0, s1, b0, b1; \ + t_uint r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ diff --git a/common/polarssl/des.c b/common/polarssl/des.c index 746752d7..b33deb6c 100644 --- a/common/polarssl/des.c +++ b/common/polarssl/des.c @@ -29,7 +29,7 @@ * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf */ -//#include "polarssl/config.h" +#include "polarssl_config.h" #define POLARSSL_DES_C #if defined(POLARSSL_DES_C) diff --git a/common/polarssl/libpcrypto.c b/common/polarssl/libpcrypto.c new file mode 100644 index 00000000..13e37f00 --- /dev/null +++ b/common/polarssl/libpcrypto.c @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 Merlok +// Copyright (C) 2018 drHatson +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// crypto commands +//----------------------------------------------------------------------------- + +#include "polarssl/libpcrypto.h" +#include +#include + +// NIST Special Publication 800-38A — Recommendation for block cipher modes of operation: methods and techniques, 2001. +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); + + aes_context aes; + aes_init(&aes); + if (aes_setkey_enc(&aes, key, 128)) + return 1; + if (aes_crypt_cbc(&aes, AES_ENCRYPT, length, iiv, input, output)) + return 2; + aes_free(&aes); + + return 0; +} + +int aes_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); + + aes_context aes; + aes_init(&aes); + if (aes_setkey_dec(&aes, key, 128)) + return 1; + if (aes_crypt_cbc(&aes, AES_DECRYPT, length, iiv, input, output)) + return 2; + aes_free(&aes); + + return 0; +} + +// 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) { + memset(mac, 0x00, 16); + uint8_t iiv[16] = {0}; + if (iv) + memcpy(iiv, iv, 16); + + // NIST 800-38B + aes_cmac128_context ctx; + aes_cmac128_starts(&ctx, key); + aes_cmac128_update(&ctx, input, length); + aes_cmac128_final(&ctx, mac); + + return 0; +} + +int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length) { + uint8_t cmac[16] = {0}; + memset(mac, 0x00, 8); + + int res = aes_cmac(iv, key, input, cmac, length); + if (res) + return res; + + for(int i = 0; i < 8; i++) + mac[i] = cmac[i * 2 + 1]; + + return 0; +} diff --git a/common/polarssl/libpcrypto.h b/common/polarssl/libpcrypto.h new file mode 100644 index 00000000..1b74c143 --- /dev/null +++ b/common/polarssl/libpcrypto.h @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// crypto commands +//----------------------------------------------------------------------------- + +#ifndef LIBPCRYPTO_H +#define LIBPCRYPTO_H + +#include +#include + +extern int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); +extern int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); +extern int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); +extern int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); + +#endif /* libpcrypto.h */ diff --git a/common/polarssl/polarssl_config.h b/common/polarssl/polarssl_config.h index 5bd88fc0..3d9a2f67 100644 --- a/common/polarssl/polarssl_config.h +++ b/common/polarssl/polarssl_config.h @@ -82,7 +82,7 @@ * * Comment to disable the use of assembly code. */ -#define POLARSSL_HAVE_ASM +//#define POLARSSL_HAVE_ASM /** * \def POLARSSL_HAVE_SSE2 @@ -762,7 +762,7 @@ * * This enables support for RSAES-OAEP and RSASSA-PSS operations. */ -#define POLARSSL_PKCS1_V21 +//#define POLARSSL_PKCS1_V21 /** * \def POLARSSL_RSA_NO_CRT diff --git a/common/polarssl/rsa.c b/common/polarssl/rsa.c new file mode 100644 index 00000000..9872274b --- /dev/null +++ b/common/polarssl/rsa.c @@ -0,0 +1,1466 @@ +/* + * The RSA public-key cryptosystem + * + * Copyright (C) 2006-2011, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * RSA was designed by Ron Rivest, Adi Shamir and Len Adleman. + * + * http://theory.lcs.mit.edu/~rivest/rsapaper.pdf + * http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf + */ + +#include "polarssl_config.h" + +#if defined(POLARSSL_RSA_C) + +#include "rsa.h" + +#if defined(POLARSSL_PKCS1_V21) +#include "md.h" +#endif + +#include +#include + +/* + * Initialize an RSA context + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id ) +{ + memset( ctx, 0, sizeof( rsa_context ) ); + + ctx->padding = padding; + ctx->hash_id = hash_id; +} + +#if defined(POLARSSL_GENPRIME) + +/* + * Generate an RSA keypair + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ) +{ + int ret; + mpi P1, Q1, H, G; + + if( f_rng == NULL || nbits < 128 || exponent < 3 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + mpi_init( &P1 ); mpi_init( &Q1 ); mpi_init( &H ); mpi_init( &G ); + + /* + * find primes P and Q with Q < P so that: + * GCD( E, (P-1)*(Q-1) ) == 1 + */ + MPI_CHK( mpi_lset( &ctx->E, exponent ) ); + + do + { + MPI_CHK( mpi_gen_prime( &ctx->P, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + MPI_CHK( mpi_gen_prime( &ctx->Q, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) < 0 ) + mpi_swap( &ctx->P, &ctx->Q ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) == 0 ) + continue; + + MPI_CHK( mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) ); + if( mpi_msb( &ctx->N ) != nbits ) + continue; + + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + } + while( mpi_cmp_int( &G, 1 ) != 0 ); + + /* + * D = E^-1 mod ((P-1)*(Q-1)) + * DP = D mod (P - 1) + * DQ = D mod (Q - 1) + * QP = Q^-1 mod P + */ + MPI_CHK( mpi_inv_mod( &ctx->D , &ctx->E, &H ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &ctx->QP, &ctx->Q, &ctx->P ) ); + + ctx->len = ( mpi_msb( &ctx->N ) + 7 ) >> 3; + +cleanup: + + mpi_free( &P1 ); mpi_free( &Q1 ); mpi_free( &H ); mpi_free( &G ); + + if( ret != 0 ) + { + rsa_free( ctx ); + return( POLARSSL_ERR_RSA_KEY_GEN_FAILED + ret ); + } + + return( 0 ); +} + +#endif + +/* + * Check a public RSA key + */ +int rsa_check_pubkey( const rsa_context *ctx ) +{ + if( !ctx->N.p || !ctx->E.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( ( ctx->N.p[0] & 1 ) == 0 || + ( ctx->E.p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->N ) < 128 || + mpi_msb( &ctx->N ) > POLARSSL_MPI_MAX_BITS ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->E ) < 2 || + mpi_msb( &ctx->E ) > 64 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + return( 0 ); +} + +/* + * Check a private RSA key + */ +int rsa_check_privkey( const rsa_context *ctx ) +{ + int ret; + mpi PQ, DE, P1, Q1, H, I, G, G2, L1, L2, DP, DQ, QP; + + if( ( ret = rsa_check_pubkey( ctx ) ) != 0 ) + return( ret ); + + if( !ctx->P.p || !ctx->Q.p || !ctx->D.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + mpi_init( &PQ ); mpi_init( &DE ); mpi_init( &P1 ); mpi_init( &Q1 ); + mpi_init( &H ); mpi_init( &I ); mpi_init( &G ); mpi_init( &G2 ); + mpi_init( &L1 ); mpi_init( &L2 ); mpi_init( &DP ); mpi_init( &DQ ); + mpi_init( &QP ); + + MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) ); + MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) ); + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + + MPI_CHK( mpi_gcd( &G2, &P1, &Q1 ) ); + MPI_CHK( mpi_div_mpi( &L1, &L2, &H, &G2 ) ); + MPI_CHK( mpi_mod_mpi( &I, &DE, &L1 ) ); + + MPI_CHK( mpi_mod_mpi( &DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &QP, &ctx->Q, &ctx->P ) ); + /* + * Check for a valid PKCS1v2 private key + */ + if( mpi_cmp_mpi( &PQ, &ctx->N ) != 0 || + mpi_cmp_mpi( &DP, &ctx->DP ) != 0 || + mpi_cmp_mpi( &DQ, &ctx->DQ ) != 0 || + mpi_cmp_mpi( &QP, &ctx->QP ) != 0 || + mpi_cmp_int( &L2, 0 ) != 0 || + mpi_cmp_int( &I, 1 ) != 0 || + mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_RSA_KEY_CHECK_FAILED; + } + +cleanup: + mpi_free( &PQ ); mpi_free( &DE ); mpi_free( &P1 ); mpi_free( &Q1 ); + mpi_free( &H ); mpi_free( &I ); mpi_free( &G ); mpi_free( &G2 ); + mpi_free( &L1 ); mpi_free( &L2 ); mpi_free( &DP ); mpi_free( &DQ ); + mpi_free( &QP ); + + if( ret == POLARSSL_ERR_RSA_KEY_CHECK_FAILED ) + return( ret ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED + ret ); + + return( 0 ); +} + +/* + * Do an RSA public key operation + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T; + + mpi_init( &T ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + olen = ctx->len; + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N, &ctx->RN ) ); + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PUBLIC_FAILED + ret ); + + return( 0 ); +} + +/* + * Do an RSA private key operation + */ +int rsa_private( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T, T1, T2; + + mpi_init( &T ); mpi_init( &T1 ); mpi_init( &T2 ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + +#if defined(POLARSSL_RSA_NO_CRT) + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) ); +#else + /* + * faster decryption using the CRT + * + * T1 = input ^ dP mod P + * T2 = input ^ dQ mod Q + */ + MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) ); + MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) ); + + /* + * T = (T1 - T2) * (Q^-1 mod P) mod P + */ + MPI_CHK( mpi_sub_mpi( &T, &T1, &T2 ) ); + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) ); + MPI_CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) ); + + /* + * output = T2 + T * Q + */ + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) ); + MPI_CHK( mpi_add_mpi( &T, &T2, &T1 ) ); +#endif + + olen = ctx->len; + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T ); mpi_free( &T1 ); mpi_free( &T2 ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PRIVATE_FAILED + ret ); + + return( 0 ); +} + +#if defined(POLARSSL_PKCS1_V21) +/** + * Generate and apply the MGF1 operation (from PKCS#1 v2.1) to a buffer. + * + * \param dst buffer to mask + * \param dlen length of destination buffer + * \param src source of the mask generation + * \param slen length of the source buffer + * \param md_ctx message digest context to use + */ +static void mgf_mask( unsigned char *dst, size_t dlen, unsigned char *src, size_t slen, + md_context_t *md_ctx ) +{ + unsigned char mask[POLARSSL_MD_MAX_SIZE]; + unsigned char counter[4]; + unsigned char *p; + unsigned int hlen; + size_t i, use_len; + + memset( mask, 0, POLARSSL_MD_MAX_SIZE ); + memset( counter, 0, 4 ); + + hlen = md_ctx->md_info->size; + + // Generate and apply dbMask + // + p = dst; + + while( dlen > 0 ) + { + use_len = hlen; + if( dlen < hlen ) + use_len = dlen; + + md_starts( md_ctx ); + md_update( md_ctx, src, slen ); + md_update( md_ctx, counter, 4 ); + md_finish( md_ctx, mask ); + + for( i = 0; i < use_len; ++i ) + *p++ ^= mask[i]; + + counter[3]++; + + dlen -= use_len; + } +} +#endif + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-ENCRYPT function + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t olen; + int ret; + unsigned char *p = output; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_info = md_info_from_type( ctx->hash_id ); + + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + hlen = md_get_size( md_info ); + + if( olen < ilen + 2 * hlen + 2 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( output, 0, olen ); + + *p++ = 0; + + // Generate a random octet string seed + // + if( ( ret = f_rng( p_rng, p, hlen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + p += hlen; + + // Construct DB + // + md( md_info, label, label_len, p ); + p += hlen; + p += olen - 2 * hlen - 2 - ilen; + *p++ = 1; + memcpy( p, input, ilen ); + + md_init_ctx( &md_ctx, md_info ); + + // maskedDB: Apply dbMask to DB + // + mgf_mask( output + hlen + 1, olen - hlen - 1, output + 1, hlen, + &md_ctx ); + + // maskedSeed: Apply seedMask to seed + // + mgf_mask( output + 1, hlen, output + hlen + 1, olen - hlen - 1, + &md_ctx ); + + md_free_ctx( &md_ctx ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, output, output ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-ENCRYPT function + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t nb_pad, olen; + int ret; + unsigned char *p = output; + + if( ctx->padding != RSA_PKCS_V15 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + if( olen < ilen + 11 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + nb_pad = olen - 3 - ilen; + + *p++ = 0; + if( mode == RSA_PUBLIC ) + { + *p++ = RSA_CRYPT; + + while( nb_pad-- > 0 ) + { + int rng_dl = 100; + + do { + ret = f_rng( p_rng, p, 1 ); + } while( *p == 0 && --rng_dl && ret == 0 ); + + // Check if RNG failed to generate data + // + if( rng_dl == 0 || ret != 0) + return POLARSSL_ERR_RSA_RNG_FAILED + ret; + + p++; + } + } + else + { + *p++ = RSA_SIGN; + + while( nb_pad-- > 0 ) + *p++ = 0xFF; + } + + *p++ = 0; + memcpy( p, input, ilen ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, output, output ) ); +} + +/* + * Add the message padding, then do an RSA operation + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_encrypt( ctx, f_rng, p_rng, mode, ilen, + input, output ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_encrypt( ctx, f_rng, p_rng, mode, NULL, 0, + ilen, input, output ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-DECRYPT function + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ) +{ + int ret; + size_t ilen; + unsigned char *p; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char lhash[POLARSSL_MD_MAX_SIZE]; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16 || ilen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + + md_init_ctx( &md_ctx, md_info ); + + // Generate lHash + // + md( md_info, label, label_len, lhash ); + + // seed: Apply seedMask to maskedSeed + // + mgf_mask( buf + 1, hlen, buf + hlen + 1, ilen - hlen - 1, + &md_ctx ); + + // DB: Apply dbMask to maskedDB + // + mgf_mask( buf + hlen + 1, ilen - hlen - 1, buf + 1, hlen, + &md_ctx ); + + p += hlen; + md_free_ctx( &md_ctx ); + + // Check validity + // + if( memcmp( lhash, p, hlen ) != 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + p += hlen; + + while( *p == 0 && p < buf + ilen ) + p++; + + if( p == buf + ilen ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if( *p++ != 0x01 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if (ilen - (p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-DECRYPT function + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + int ret, correct = 1; + size_t ilen, pad_count = 0; + unsigned char *p, *q; + unsigned char bt; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16 || ilen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 ) + correct = 0; + + bt = *p++; + if( ( bt != RSA_CRYPT && mode == RSA_PRIVATE ) || + ( bt != RSA_SIGN && mode == RSA_PUBLIC ) ) + { + correct = 0; + } + + if( bt == RSA_CRYPT ) + { + while( *p != 0 && p < buf + ilen - 1 ) + pad_count += ( *p++ != 0 ); + + correct &= ( *p == 0 && p < buf + ilen - 1 ); + + q = p; + + // Also pass over all other bytes to reduce timing differences + // + while ( q < buf + ilen - 1 ) + pad_count += ( *q++ != 0 ); + + // Prevent compiler optimization of pad_count + // + correct |= pad_count & 0x100000; /* Always 0 unless 1M bit keys */ + p++; + } + else + { + while( *p == 0xFF && p < buf + ilen - 1 ) + pad_count += ( *p++ == 0xFF ); + + correct &= ( *p == 0 && p < buf + ilen - 1 ); + + q = p; + + // Also pass over all other bytes to reduce timing differences + // + while ( q < buf + ilen - 1 ) + pad_count += ( *q++ != 0 ); + + // Prevent compiler optimization of pad_count + // + correct |= pad_count & 0x100000; /* Always 0 unless 1M bit keys */ + p++; + } + + if( correct == 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if (ilen - (p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} + +/* + * Do an RSA operation, then remove the message padding + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_decrypt( ctx, mode, olen, input, output, + output_max_len ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_decrypt( ctx, mode, NULL, 0, olen, input, + output, output_max_len ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-SIGN function + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t olen; + unsigned char *p = sig; + unsigned char salt[POLARSSL_MD_MAX_SIZE]; + unsigned int slen, hlen, offset = 0; + int ret; + size_t msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + switch( hash_id ) + { + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + hashlen = 16; + break; + + case SIG_RSA_SHA1: + hashlen = 20; + break; + + case SIG_RSA_SHA224: + hashlen = 28; + break; + + case SIG_RSA_SHA256: + hashlen = 32; + break; + + case SIG_RSA_SHA384: + hashlen = 48; + break; + + case SIG_RSA_SHA512: + hashlen = 64; + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = hlen; + + if( olen < hlen + slen + 2 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( sig, 0, olen ); + + msb = mpi_msb( &ctx->N ) - 1; + + // Generate salt of length slen + // + if( ( ret = f_rng( p_rng, salt, slen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + // Note: EMSA-PSS encoding is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + p += olen - hlen * 2 - 2; + *p++ = 0x01; + memcpy( p, salt, slen ); + p += slen; + + md_init_ctx( &md_ctx, md_info ); + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, p, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, salt, slen ); + md_finish( &md_ctx, p ); + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + offset = 1; + + // maskedDB: Apply dbMask to DB + // + mgf_mask( sig + offset, olen - hlen - 1 - offset, p, hlen, &md_ctx ); + + md_free_ctx( &md_ctx ); + + msb = mpi_msb( &ctx->N ) - 1; + sig[0] &= 0xFF >> ( olen * 8 - msb ); + + p += hlen; + *p++ = 0xBC; + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, sig, sig ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-V1_5-SIGN function + */ +/* + * Do an RSA operation to sign the message digest + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t nb_pad, olen; + unsigned char *p = sig; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + switch( hash_id ) + { + case SIG_RSA_RAW: + nb_pad = olen - 3 - hashlen; + break; + + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + nb_pad = olen - 3 - 34; + break; + + case SIG_RSA_SHA1: + nb_pad = olen - 3 - 35; + break; + + case SIG_RSA_SHA224: + nb_pad = olen - 3 - 47; + break; + + case SIG_RSA_SHA256: + nb_pad = olen - 3 - 51; + break; + + case SIG_RSA_SHA384: + nb_pad = olen - 3 - 67; + break; + + case SIG_RSA_SHA512: + nb_pad = olen - 3 - 83; + break; + + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + if( ( nb_pad < 8 ) || ( nb_pad > olen ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + *p++ = 0; + *p++ = RSA_SIGN; + memset( p, 0xFF, nb_pad ); + p += nb_pad; + *p++ = 0; + + switch( hash_id ) + { + case SIG_RSA_RAW: + memcpy( p, hash, hashlen ); + break; + + case SIG_RSA_MD2: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 2; break; + + case SIG_RSA_MD4: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 4; break; + + case SIG_RSA_MD5: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 5; break; + + case SIG_RSA_SHA1: + memcpy( p, ASN1_HASH_SHA1, 15 ); + memcpy( p + 15, hash, 20 ); + break; + + case SIG_RSA_SHA224: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 28 ); + p[1] += 28; p[14] = 4; p[18] += 28; break; + + case SIG_RSA_SHA256: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 32 ); + p[1] += 32; p[14] = 1; p[18] += 32; break; + + case SIG_RSA_SHA384: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 48 ); + p[1] += 48; p[14] = 2; p[18] += 48; break; + + case SIG_RSA_SHA512: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 64 ); + p[1] += 64; p[14] = 3; p[18] += 64; break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, sig, sig ) ); +} + +/* + * Do an RSA operation to sign the message digest + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_sign( ctx, mode, hash_id, + hashlen, hash, sig ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_sign( ctx, f_rng, p_rng, mode, hash_id, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-VERIFY function + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + int ret; + size_t siglen; + unsigned char *p; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char result[POLARSSL_MD_MAX_SIZE]; + unsigned char zeros[8]; + unsigned int hlen; + size_t slen, msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( buf[siglen - 1] != 0xBC ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + switch( hash_id ) + { + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + hashlen = 16; + break; + + case SIG_RSA_SHA1: + hashlen = 20; + break; + + case SIG_RSA_SHA224: + hashlen = 28; + break; + + case SIG_RSA_SHA256: + hashlen = 32; + break; + + case SIG_RSA_SHA384: + hashlen = 48; + break; + + case SIG_RSA_SHA512: + hashlen = 64; + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = siglen - hlen - 1; + + memset( zeros, 0, 8 ); + + // Note: EMSA-PSS verification is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + { + p++; + siglen -= 1; + } + if( buf[0] >> ( 8 - siglen * 8 + msb ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_init_ctx( &md_ctx, md_info ); + + mgf_mask( p, siglen - hlen - 1, p + siglen - hlen - 1, hlen, &md_ctx ); + + buf[0] &= 0xFF >> ( siglen * 8 - msb ); + + while( *p == 0 && p < buf + siglen ) + p++; + + if( p == buf + siglen || + *p++ != 0x01 ) + { + md_free_ctx( &md_ctx ); + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + slen -= p - buf; + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, zeros, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, p, slen ); + md_finish( &md_ctx, result ); + + md_free_ctx( &md_ctx ); + + if( memcmp( p + slen, result, hlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-v1_5-VERIFY function + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + int ret; + size_t len, siglen; + unsigned char *p, c; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 || *p++ != RSA_SIGN ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + while( *p != 0 ) + { + if( p >= buf + siglen - 1 || *p != 0xFF ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + p++; + } + p++; + + len = siglen - ( p - buf ); + + if( len == 33 && hash_id == SIG_RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1_ALT, 13 ) == 0 && + memcmp( p + 13, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + if( len == 34 ) + { + c = p[13]; + p[13] = 0; + + if( memcmp( p, ASN1_HASH_MDX, 18 ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( c == 2 && hash_id == SIG_RSA_MD2 ) || + ( c == 4 && hash_id == SIG_RSA_MD4 ) || + ( c == 5 && hash_id == SIG_RSA_MD5 ) ) + { + if( memcmp( p + 18, hash, 16 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + } + + if( len == 35 && hash_id == SIG_RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1, 15 ) == 0 && + memcmp( p + 15, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + if( ( len == 19 + 28 && p[14] == 4 && hash_id == SIG_RSA_SHA224 ) || + ( len == 19 + 32 && p[14] == 1 && hash_id == SIG_RSA_SHA256 ) || + ( len == 19 + 48 && p[14] == 2 && hash_id == SIG_RSA_SHA384 ) || + ( len == 19 + 64 && p[14] == 3 && hash_id == SIG_RSA_SHA512 ) ) + { + c = p[1] - 17; + p[1] = 17; + p[14] = 0; + + if( p[18] == c && + memcmp( p, ASN1_HASH_SHA2X, 18 ) == 0 && + memcmp( p + 19, hash, c ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + if( len == hashlen && hash_id == SIG_RSA_RAW ) + { + if( memcmp( p, hash, hashlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); +} + +/* + * Do an RSA operation and check the message digest + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_verify( ctx, mode, hash_id, + hashlen, hash, sig ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_verify( ctx, mode, hash_id, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +/* + * Free the components of an RSA key + */ +void rsa_free( rsa_context *ctx ) +{ + mpi_free( &ctx->RQ ); mpi_free( &ctx->RP ); mpi_free( &ctx->RN ); + mpi_free( &ctx->QP ); mpi_free( &ctx->DQ ); mpi_free( &ctx->DP ); + mpi_free( &ctx->Q ); mpi_free( &ctx->P ); mpi_free( &ctx->D ); + mpi_free( &ctx->E ); mpi_free( &ctx->N ); +} + +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/sha1.h" + +/* + * Example RSA-1024 keypair, for test purposes + */ +#define KEY_LEN 128 + +#define RSA_N "9292758453063D803DD603D5E777D788" \ + "8ED1D5BF35786190FA2F23EBC0848AEA" \ + "DDA92CA6C3D80B32C4D109BE0F36D6AE" \ + "7130B9CED7ACDF54CFC7555AC14EEBAB" \ + "93A89813FBF3C4F8066D2D800F7C38A8" \ + "1AE31942917403FF4946B0A83D3D3E05" \ + "EE57C6F5F5606FB5D4BC6CD34EE0801A" \ + "5E94BB77B07507233A0BC7BAC8F90F79" + +#define RSA_E "10001" + +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ + "3C94D22288ACD763FD8E5600ED4A702D" \ + "F84198A5F06C2E72236AE490C93F07F8" \ + "3CC559CD27BC2D1CA488811730BB5725" + +#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ + "D8AAEA56749EA28623272E4F7D0592AF" \ + "7C1F1313CAC9471B5C523BFE592F517B" \ + "407A1BD76C164B93DA2D32A383E58357" + +#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ + "F38D18D2B2F0E2DD275AA977E2BF4411" \ + "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ + "A74206CEC169D74BF5A8C50D6F48EA08" + +#define PT_LEN 24 +#define RSA_PT "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \ + "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD" + +static int myrand( void *rng_state, unsigned char *output, size_t len ) +{ + size_t i; + + if( rng_state != NULL ) + rng_state = NULL; + + for( i = 0; i < len; ++i ) + output[i] = rand(); + + return( 0 ); +} + +/* + * Checkup routine + */ +int rsa_self_test( int verbose ) +{ + size_t len; + rsa_context rsa; + unsigned char rsa_plaintext[PT_LEN]; + unsigned char rsa_decrypted[PT_LEN]; + unsigned char rsa_ciphertext[KEY_LEN]; +#if defined(POLARSSL_SHA1_C) + unsigned char sha1sum[20]; +#endif + + rsa_init( &rsa, RSA_PKCS_V15, 0 ); + + rsa.len = KEY_LEN; + mpi_read_string( &rsa.N , 16, RSA_N ); + mpi_read_string( &rsa.E , 16, RSA_E ); + mpi_read_string( &rsa.D , 16, RSA_D ); + mpi_read_string( &rsa.P , 16, RSA_P ); + mpi_read_string( &rsa.Q , 16, RSA_Q ); + mpi_read_string( &rsa.DP, 16, RSA_DP ); + mpi_read_string( &rsa.DQ, 16, RSA_DQ ); + mpi_read_string( &rsa.QP, 16, RSA_QP ); + + if( verbose != 0 ) + printf( " RSA key validation: " ); + + if( rsa_check_pubkey( &rsa ) != 0 || + rsa_check_privkey( &rsa ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 encryption : " ); + + memcpy( rsa_plaintext, RSA_PT, PT_LEN ); + + if( rsa_pkcs1_encrypt( &rsa, &myrand, NULL, RSA_PUBLIC, PT_LEN, + rsa_plaintext, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 decryption : " ); + + if( rsa_pkcs1_decrypt( &rsa, RSA_PRIVATE, &len, + rsa_ciphertext, rsa_decrypted, + sizeof(rsa_decrypted) ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + +#if defined(POLARSSL_SHA1_C) + if( verbose != 0 ) + printf( "passed\n PKCS#1 data sign : " ); + + sha1( rsa_plaintext, PT_LEN, sha1sum ); + + if( rsa_pkcs1_sign( &rsa, NULL, NULL, RSA_PRIVATE, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 sig. verify: " ); + + if( rsa_pkcs1_verify( &rsa, RSA_PUBLIC, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n\n" ); +#endif /* POLARSSL_SHA1_C */ + + rsa_free( &rsa ); + + return( 0 ); +} + +#endif + +#endif diff --git a/common/polarssl/rsa.h b/common/polarssl/rsa.h new file mode 100644 index 00000000..f9a02202 --- /dev/null +++ b/common/polarssl/rsa.h @@ -0,0 +1,597 @@ +/** + * \file rsa.h + * + * \brief The RSA public-key cryptosystem + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_RSA_H +#define POLARSSL_RSA_H + +#include "bignum.h" + +/* + * RSA Error codes + */ +#define POLARSSL_ERR_RSA_BAD_INPUT_DATA -0x4080 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_RSA_INVALID_PADDING -0x4100 /**< Input data contains invalid padding and is rejected. */ +#define POLARSSL_ERR_RSA_KEY_GEN_FAILED -0x4180 /**< Something failed during generation of a key. */ +#define POLARSSL_ERR_RSA_KEY_CHECK_FAILED -0x4200 /**< Key failed to pass the libraries validity check. */ +#define POLARSSL_ERR_RSA_PUBLIC_FAILED -0x4280 /**< The public key operation failed. */ +#define POLARSSL_ERR_RSA_PRIVATE_FAILED -0x4300 /**< The private key operation failed. */ +#define POLARSSL_ERR_RSA_VERIFY_FAILED -0x4380 /**< The PKCS#1 verification failed. */ +#define POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE -0x4400 /**< The output buffer for decryption is not large enough. */ +#define POLARSSL_ERR_RSA_RNG_FAILED -0x4480 /**< The random generator failed to generate non-zeros. */ + +/* + * PKCS#1 constants + */ +#define SIG_RSA_RAW 0 +#define SIG_RSA_MD2 2 +#define SIG_RSA_MD4 3 +#define SIG_RSA_MD5 4 +#define SIG_RSA_SHA1 5 +#define SIG_RSA_SHA224 14 +#define SIG_RSA_SHA256 11 +#define SIG_RSA_SHA384 12 +#define SIG_RSA_SHA512 13 + +#define RSA_PUBLIC 0 +#define RSA_PRIVATE 1 + +#define RSA_PKCS_V15 0 +#define RSA_PKCS_V21 1 + +#define RSA_SIGN 1 +#define RSA_CRYPT 2 + +#define ASN1_STR_CONSTRUCTED_SEQUENCE "\x30" +#define ASN1_STR_NULL "\x05" +#define ASN1_STR_OID "\x06" +#define ASN1_STR_OCTET_STRING "\x04" + +#define OID_DIGEST_ALG_MDX "\x2A\x86\x48\x86\xF7\x0D\x02\x00" +#define OID_HASH_ALG_SHA1 "\x2b\x0e\x03\x02\x1a" +#define OID_HASH_ALG_SHA2X "\x60\x86\x48\x01\x65\x03\x04\x02\x00" + +#define OID_ISO_MEMBER_BODIES "\x2a" +#define OID_ISO_IDENTIFIED_ORG "\x2b" + +/* + * ISO Member bodies OID parts + */ +#define OID_COUNTRY_US "\x86\x48" +#define OID_RSA_DATA_SECURITY "\x86\xf7\x0d" + +/* + * ISO Identified organization OID parts + */ +#define OID_OIW_SECSIG_SHA1 "\x0e\x03\x02\x1a" + +/* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ +#define ASN1_HASH_MDX \ +( \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x20" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0C" \ + ASN1_STR_OID "\x08" \ + OID_DIGEST_ALG_MDX \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x10" \ +) + +#define ASN1_HASH_SHA1 \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x21" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x09" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA1_ALT \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x1F" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x07" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA2X \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x11" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0d" \ + ASN1_STR_OID "\x09" \ + OID_HASH_ALG_SHA2X \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x00" + +/** + * \brief RSA context structure + */ +typedef struct +{ + int ver; /*!< always 0 */ + size_t len; /*!< size(N) in chars */ + + mpi N; /*!< public modulus */ + mpi E; /*!< public exponent */ + + mpi D; /*!< private exponent */ + mpi P; /*!< 1st prime factor */ + mpi Q; /*!< 2nd prime factor */ + mpi DP; /*!< D % (P - 1) */ + mpi DQ; /*!< D % (Q - 1) */ + mpi QP; /*!< 1 / (Q % P) */ + + mpi RN; /*!< cached R^2 mod N */ + mpi RP; /*!< cached R^2 mod P */ + mpi RQ; /*!< cached R^2 mod Q */ + + int padding; /*!< RSA_PKCS_V15 for 1.5 padding and + RSA_PKCS_v21 for OAEP/PSS */ + int hash_id; /*!< Hash identifier of md_type_t as + specified in the md.h header file + for the EME-OAEP and EMSA-PSS + encoding */ +} +rsa_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize an RSA context + * + * Note: Set padding to RSA_PKCS_V21 for the RSAES-OAEP + * encryption scheme and the RSASSA-PSS signature scheme. + * + * \param ctx RSA context to be initialized + * \param padding RSA_PKCS_V15 or RSA_PKCS_V21 + * \param hash_id RSA_PKCS_V21 hash identifier + * + * \note The hash_id parameter is actually ignored + * when using RSA_PKCS_V15 padding. + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id); + +/** + * \brief Generate an RSA keypair + * + * \param ctx RSA context that will hold the key + * \param f_rng RNG function + * \param p_rng RNG parameter + * \param nbits size of the public key in bits + * \param exponent public exponent (e.g., 65537) + * + * \note rsa_init() must be called beforehand to setup + * the RSA context. + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ); + +/** + * \brief Check a public RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_pubkey( const rsa_context *ctx ); + +/** + * \brief Check a private RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_privkey( const rsa_context *ctx ); + +/** + * \brief Do an RSA public key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note This function does NOT take care of message + * padding. Also, be sure to set input[0] = 0 or assure that + * input is smaller than N. + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Do an RSA private key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_private( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 encryption using the + * mode from the context. Add the message padding, then do an + * RSA operation. + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v1.5 encryption (RSAES-PKCS1-v1_5-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP encryption (RSAES-OAEP-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 decryption using the + * mode from the context. Do an RSA operation, then remove + * the message padding + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v1.5 decryption (RSAES-PKCS1-v1_5-DECRYPT) + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP decryption (RSAES-OAEP-DECRYPT) + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Generic wrapper to perform a PKCS#1 signature using the + * mode from the context. Do a private RSA operation to sign + * a message digest + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * encoding. hash_id in the function call is the type of hash + * that is encoded. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 signature (RSASSA-PKCS1-v1_5-SIGN) + * + * \param ctx RSA context + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS signature (RSASSA-PSS-SIGN) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * encoding. hash_id in the function call is the type of hash + * that is encoded. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Generic wrapper to perform a PKCS#1 verification using the + * mode from the context. Do a public RSA operation and check + * the message digest + * + * \param ctx points to an RSA public key + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * verification. hash_id in the function call is the type of hash + * that is verified. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 verification (RSASSA-PKCS1-v1_5-VERIFY) + * + * \param ctx points to an RSA public key + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS verification (RSASSA-PSS-VERIFY) + * \brief Do a public RSA and check the message digest + * + * \param ctx points to an RSA public key + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * verification. hash_id in the function call is the type of hash + * that is verified. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Free the components of an RSA key + * + * \param ctx RSA Context to free + */ +void rsa_free( rsa_context *ctx ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int rsa_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* rsa.h */ diff --git a/common/sha1.c b/common/polarssl/sha1.c similarity index 87% rename from common/sha1.c rename to common/polarssl/sha1.c index d20c54a4..28bcd4e3 100644 --- a/common/sha1.c +++ b/common/polarssl/sha1.c @@ -1,8 +1,12 @@ /* * FIPS-180-1 compliant SHA-1 implementation * - * Copyright (C) 2006-2014, ARM Limited, All Rights Reserved - * This file is part of mbed TLS (https://tls.mbed.org) + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,38 +28,16 @@ * http://www.itl.nist.gov/fipspubs/fip180-1.htm */ -#if !defined(POLARSSL_CONFIG_FILE) -//#include "polarssl/config.h" -#define POLARSSL_SHA1_C - -#else -#include POLARSSL_CONFIG_FILE -#endif +#include "polarssl_config.h" #if defined(POLARSSL_SHA1_C) #include "sha1.h" -#include - -#if defined(POLARSSL_FS_IO) +#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) #include #endif -#if defined(POLARSSL_SELF_TEST) -#if defined(POLARSSL_PLATFORM_C) -#include "polarssl/platform.h" -#else -#include -#define polarssl_printf printf -#endif /* POLARSSL_PLATFORM_C */ -#endif /* POLARSSL_SELF_TEST */ - -/* Implementation that should never be optimized out by the compiler */ -static void polarssl_zeroize( void *v, size_t n ) { - volatile unsigned char *p = v; while( n-- ) *p++ = 0; -} - #if !defined(POLARSSL_SHA1_ALT) /* @@ -81,19 +63,6 @@ static void polarssl_zeroize( void *v, size_t n ) { } #endif -void sha1_init( sha1_context *ctx ) -{ - memset( ctx, 0, sizeof( sha1_context ) ); -} - -void sha1_free( sha1_context *ctx ) -{ - if( ctx == NULL ) - return; - - polarssl_zeroize( ctx, sizeof( sha1_context ) ); -} - /* * SHA-1 context setup */ @@ -134,8 +103,8 @@ void sha1_process( sha1_context *ctx, const unsigned char data[64] ) #define R(t) \ ( \ - temp = W[( t - 3 ) & 0x0F] ^ W[( t - 8 ) & 0x0F] ^ \ - W[( t - 14 ) & 0x0F] ^ W[ t & 0x0F], \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ ( W[t & 0x0F] = S(temp,1) ) \ ) @@ -273,7 +242,7 @@ void sha1_update( sha1_context *ctx, const unsigned char *input, size_t ilen ) size_t fill; uint32_t left; - if( ilen == 0 ) + if( ilen <= 0 ) return; left = ctx->total[0] & 0x3F; @@ -351,11 +320,11 @@ void sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ) { sha1_context ctx; - sha1_init( &ctx ); sha1_starts( &ctx ); sha1_update( &ctx, input, ilen ); sha1_finish( &ctx, output ); - sha1_free( &ctx ); + + memset( &ctx, 0, sizeof( sha1_context ) ); } #if defined(POLARSSL_FS_IO) @@ -372,14 +341,14 @@ int sha1_file( const char *path, unsigned char output[20] ) if( ( f = fopen( path, "rb" ) ) == NULL ) return( POLARSSL_ERR_SHA1_FILE_IO_ERROR ); - sha1_init( &ctx ); sha1_starts( &ctx ); while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) sha1_update( &ctx, buf, n ); sha1_finish( &ctx, output ); - sha1_free( &ctx ); + + memset( &ctx, 0, sizeof( sha1_context ) ); if( ferror( f ) != 0 ) { @@ -395,8 +364,7 @@ int sha1_file( const char *path, unsigned char output[20] ) /* * SHA-1 HMAC context setup */ -void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, - size_t keylen ) +void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, size_t keylen ) { size_t i; unsigned char sum[20]; @@ -420,14 +388,13 @@ void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, sha1_starts( ctx ); sha1_update( ctx, ctx->ipad, 64 ); - polarssl_zeroize( sum, sizeof( sum ) ); + memset( sum, 0, sizeof( sum ) ); } /* * SHA-1 HMAC process buffer */ -void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, - size_t ilen ) +void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, size_t ilen ) { sha1_update( ctx, input, ilen ); } @@ -445,7 +412,7 @@ void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ) sha1_update( ctx, tmpbuf, 20 ); sha1_finish( ctx, output ); - polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); + memset( tmpbuf, 0, sizeof( tmpbuf ) ); } /* @@ -466,18 +433,18 @@ void sha1_hmac( const unsigned char *key, size_t keylen, { sha1_context ctx; - sha1_init( &ctx ); sha1_hmac_starts( &ctx, key, keylen ); sha1_hmac_update( &ctx, input, ilen ); sha1_hmac_finish( &ctx, output ); - sha1_free( &ctx ); + + memset( &ctx, 0, sizeof( sha1_context ) ); } #if defined(POLARSSL_SELF_TEST) /* * FIPS-180-1 test vectors */ -static const unsigned char sha1_test_buf[3][57] = +static unsigned char sha1_test_buf[3][57] = { { "abc" }, { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, @@ -502,7 +469,7 @@ static const unsigned char sha1_test_sum[3][20] = /* * RFC 2202 test vectors */ -static const unsigned char sha1_hmac_test_key[7][26] = +static unsigned char sha1_hmac_test_key[7][26] = { { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" "\x0B\x0B\x0B\x0B" }, @@ -522,7 +489,7 @@ static const int sha1_hmac_test_keylen[7] = 20, 4, 20, 25, 20, 80, 80 }; -static const unsigned char sha1_hmac_test_buf[7][74] = +static unsigned char sha1_hmac_test_buf[7][74] = { { "Hi There" }, { "what do ya want for nothing?" }, @@ -570,20 +537,18 @@ static const unsigned char sha1_hmac_test_sum[7][20] = */ int sha1_self_test( int verbose ) { - int i, j, buflen, ret = 0; + int i, j, buflen; unsigned char buf[1024]; unsigned char sha1sum[20]; sha1_context ctx; - sha1_init( &ctx ); - /* * SHA-1 */ for( i = 0; i < 3; i++ ) { if( verbose != 0 ) - polarssl_printf( " SHA-1 test #%d: ", i + 1 ); + printf( " SHA-1 test #%d: ", i + 1 ); sha1_starts( &ctx ); @@ -603,27 +568,26 @@ int sha1_self_test( int verbose ) if( memcmp( sha1sum, sha1_test_sum[i], 20 ) != 0 ) { if( verbose != 0 ) - polarssl_printf( "failed\n" ); + printf( "failed\n" ); - ret = 1; - goto exit; + return( 1 ); } if( verbose != 0 ) - polarssl_printf( "passed\n" ); + printf( "passed\n" ); } if( verbose != 0 ) - polarssl_printf( "\n" ); + printf( "\n" ); for( i = 0; i < 7; i++ ) { if( verbose != 0 ) - polarssl_printf( " HMAC-SHA-1 test #%d: ", i + 1 ); + printf( " HMAC-SHA-1 test #%d: ", i + 1 ); if( i == 5 || i == 6 ) { - memset( buf, 0xAA, buflen = 80 ); + memset( buf, '\xAA', buflen = 80 ); sha1_hmac_starts( &ctx, buf, buflen ); } else @@ -640,26 +604,21 @@ int sha1_self_test( int verbose ) if( memcmp( sha1sum, sha1_hmac_test_sum[i], buflen ) != 0 ) { if( verbose != 0 ) - polarssl_printf( "failed\n" ); + printf( "failed\n" ); - ret = 1; - goto exit; + return( 1 ); } if( verbose != 0 ) - polarssl_printf( "passed\n" ); + printf( "passed\n" ); } if( verbose != 0 ) - polarssl_printf( "\n" ); + printf( "\n" ); -exit: - sha1_free( &ctx ); - - return( ret ); + return( 0 ); } -#endif /* POLARSSL_SELF_TEST */ - -#endif /* POLARSSL_SHA1_C */ +#endif +#endif diff --git a/common/sha1.h b/common/polarssl/sha1.h similarity index 81% rename from common/sha1.h rename to common/polarssl/sha1.h index 056bba7e..01cb69b7 100644 --- a/common/sha1.h +++ b/common/polarssl/sha1.h @@ -3,9 +3,12 @@ * * \brief SHA-1 cryptographic hash function * - * Copyright (C) 2006-2014, ARM Limited, All Rights Reserved + * Copyright (C) 2006-2013, Brainspark B.V. * - * This file is part of mbed TLS (https://tls.mbed.org) + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,31 +27,11 @@ #ifndef POLARSSL_SHA1_H #define POLARSSL_SHA1_H -#if !defined(POLARSSL_CONFIG_FILE) -//#include "config.h" -/** - * \def POLARSSL_SHA1_C - * - * Enable the SHA1 cryptographic hash algorithm. - * - * Module: library/sha1.c - * Caller: library/md.c - * library/ssl_cli.c - * library/ssl_srv.c - * library/ssl_tls.c - * library/x509write_crt.c - * - * This module is required for SSL/TLS and SHA1-signed certificates. - */ -#define POLARSSL_SHA1_C +#include "polarssl_config.h" -#else -#include POLARSSL_CONFIG_FILE -#endif +#include -#include - -#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#ifdef _MSC_VER #include typedef UINT32 uint32_t; #else @@ -61,10 +44,6 @@ typedef UINT32 uint32_t; // Regular implementation // -#ifdef __cplusplus -extern "C" { -#endif - /** * \brief SHA-1 context structure */ @@ -79,19 +58,9 @@ typedef struct } sha1_context; -/** - * \brief Initialize SHA-1 context - * - * \param ctx SHA-1 context to be initialized - */ -void sha1_init( sha1_context *ctx ); - -/** - * \brief Clear SHA-1 context - * - * \param ctx SHA-1 context to be cleared - */ -void sha1_free( sha1_context *ctx ); +#ifdef __cplusplus +extern "C" { +#endif /** * \brief SHA-1 context setup @@ -158,8 +127,7 @@ int sha1_file( const char *path, unsigned char output[20] ); * \param key HMAC secret key * \param keylen length of the HMAC key */ -void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, - size_t keylen ); +void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, size_t keylen ); /** * \brief SHA-1 HMAC process buffer @@ -168,8 +136,7 @@ void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, * \param input buffer holding the data * \param ilen length of the input data */ -void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, - size_t ilen ); +void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, size_t ilen ); /** * \brief SHA-1 HMAC final digest diff --git a/common/protocols.h b/common/protocols.h index 31252ad3..06a80de1 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -167,22 +167,50 @@ NXP/Philips CUSTOM COMMANDS #define ISO14443B_COMPLETION 0x0F #define ISO14443B_AUTHENTICATE 0x0A -//First byte is 26 -#define ISO15693_INVENTORY 0x01 -#define ISO15693_STAYQUIET 0x02 -//First byte is 02 -#define ISO15693_READBLOCK 0x20 -#define ISO15693_WRITEBLOCK 0x21 -#define ISO15693_LOCKBLOCK 0x22 -#define ISO15693_READ_MULTI_BLOCK 0x23 -#define ISO15693_SELECT 0x25 -#define ISO15693_RESET_TO_READY 0x26 -#define ISO15693_WRITE_AFI 0x27 -#define ISO15693_LOCK_AFI 0x28 -#define ISO15693_WRITE_DSFID 0x29 -#define ISO15693_LOCK_DSFID 0x2A -#define ISO15693_GET_SYSTEM_INFO 0x2B -#define ISO15693_READ_MULTI_SECSTATUS 0x2C +// ISO15693 COMMANDS +#define ISO15693_INVENTORY 0x01 +#define ISO15693_STAYQUIET 0x02 +#define ISO15693_READBLOCK 0x20 +#define ISO15693_WRITEBLOCK 0x21 +#define ISO15693_LOCKBLOCK 0x22 +#define ISO15693_READ_MULTI_BLOCK 0x23 +#define ISO15693_SELECT 0x25 +#define ISO15693_RESET_TO_READY 0x26 +#define ISO15693_WRITE_AFI 0x27 +#define ISO15693_LOCK_AFI 0x28 +#define ISO15693_WRITE_DSFID 0x29 +#define ISO15693_LOCK_DSFID 0x2A +#define ISO15693_GET_SYSTEM_INFO 0x2B +#define ISO15693_READ_MULTI_SECSTATUS 0x2C + +// ISO15693 REQUEST FLAGS +#define ISO15693_REQ_SUBCARRIER_TWO (1<<0) +#define ISO15693_REQ_DATARATE_HIGH (1<<1) +#define ISO15693_REQ_INVENTORY (1<<2) +#define ISO15693_REQ_PROTOCOL_EXT (1<<3) // RFU +#define ISO15693_REQ_OPTION (1<<6) // Command specific option selector +// when REQ_INVENTORY is not set +#define ISO15693_REQ_SELECT (1<<4) // only selected cards response +#define ISO15693_REQ_ADDRESS (1<<5) // this req contains an address +// when REQ_INVENTORY is set +#define ISO15693_REQINV_AFI (1<<4) // AFI Field is present +#define ISO15693_REQINV_SLOT1 (1<<5) // 1 Slot (16 slots if not set) + +// ISO15693 RESPONSE FLAGS +#define ISO15693_RES_ERROR (1<<0) +#define ISO15693_RES_EXT (1<<3) // Protocol Extention + +// ISO15693 RESPONSE ERROR CODES +#define ISO15693_NOERROR 0x00 +#define ISO15693_ERROR_CMD_NOT_SUP 0x01 // Command not supported +#define ISO15693_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error) +#define ISO15693_ERROR_CMD_OPTION 0x03 // Command option not supported +#define ISO15693_ERROR_GENERIC 0x0F // No additional Info about this error +#define ISO15693_ERROR_BLOCK_UNAVAILABLE 0x10 +#define ISO15693_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again +#define ISO15693_ERROR_BLOCK_LOCKED 0x12 // cannot be changed +#define ISO15693_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful +#define ISO15693_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful // Topaz command set: @@ -200,10 +228,12 @@ NXP/Philips CUSTOM COMMANDS #define TOPAZ_WRITE_NE8 0x1B // Write-no-erase (eight bytes) -#define ISO_14443A 0 -#define ICLASS 1 -#define ISO_14443B 2 -#define TOPAZ 3 +#define ISO_14443A 0 +#define ICLASS 1 +#define ISO_14443B 2 +#define TOPAZ 3 +#define PROTO_MIFARE 4 +#define ISO_7816_4 5 //-- Picopass fuses #define FUSE_FPERS 0x80 @@ -215,6 +245,29 @@ NXP/Philips CUSTOM COMMANDS #define FUSE_FPROD0 0x02 #define FUSE_RA 0x01 +// 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 0xB4 +#define ISO7816_MANAGE_CHANNEL 0x70 +// ISO7816-4 For response APDU's +#define ISO7816_OK 0x9000 +// 6x xx = ERROR + + + void printIclassDumpInfo(uint8_t* iclass_dump); void getMemConfig(uint8_t mem_cfg, uint8_t chip_cfg, uint8_t *max_blk, uint8_t *app_areas, uint8_t *kb); diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 84aa2c06..d33bca7b 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -38,25 +38,31 @@ #define AT91C_EP_CONTROL 0 -#define AT91C_EP_IN_SIZE 0x40 #define AT91C_EP_OUT 1 -#define AT91C_EP_OUT_SIZE 0x40 #define AT91C_EP_IN 2 +#define AT91C_EP_NOTIFY 3 +#define AT91C_EP_OUT_SIZE 0x40 +#define AT91C_EP_IN_SIZE 0x40 + +// Language must always be 0. +#define STR_LANGUAGE_CODES 0x00 +#define STR_MANUFACTURER 0x01 +#define STR_PRODUCT 0x02 static const char devDescriptor[] = { /* Device descriptor */ 0x12, // bLength 0x01, // bDescriptorType 0x00,0x02, // Complies with USB Spec. Release (0200h = release 2.0) - 0x02, // bDeviceClass: CDC class code - 0x00, // bDeviceSubclass: CDC class sub code - 0x00, // bDeviceProtocol: CDC Device protocol + 0x02, // bDeviceClass: (Communication Device Class) + 0x00, // bDeviceSubclass: (unused at this time) + 0x00, // bDeviceProtocol: (unused at this time) 0x08, // bMaxPacketSize0 0xc4,0x9a, // Vendor ID (0x9ac4 = J. Westhues) 0x8f,0x4b, // Product ID (0x4b8f = Proxmark-3 RFID Instrument) 0x01,0x00, // Device release number (0001) - 0x01, // iManufacturer - 0x02, // iProduct + STR_MANUFACTURER, // iManufacturer + STR_PRODUCT, // iProduct 0x00, // iSerialNumber 0x01 // bNumConfigs }; @@ -71,82 +77,81 @@ static const char cfgDescriptor[] = { 0x02, // CbNumInterfaces 0x01, // CbConfigurationValue 0x00, // CiConfiguration - 0xC0, // CbmAttributes 0xA0 - 0xFA, // CMaxPower + 0x80, // CbmAttributes (Bus Powered) + 0x4B, // CMaxPower (150mA max current drawn from bus) - /* Communication Class Interface Descriptor Requirement */ + /* Interface 0 Descriptor: Communication Class Interface */ 0x09, // bLength 0x04, // bDescriptorType 0x00, // bInterfaceNumber 0x00, // bAlternateSetting 0x01, // bNumEndpoints - 0x02, // bInterfaceClass - 0x02, // bInterfaceSubclass - 0x01, // bInterfaceProtocol + 0x02, // bInterfaceClass: Communication Interface Class + 0x02, // bInterfaceSubclass: Abstract Control Model + 0x01, // bInterfaceProtocol: Common AT Commands, V.25ter 0x00, // iInterface /* Header Functional Descriptor */ 0x05, // bFunction Length - 0x24, // bDescriptor type: CS_INTERFACE - 0x00, // bDescriptor subtype: Header Func Desc + 0x24, // bDescriptor type: CS_INTERFACE + 0x00, // bDescriptor subtype: Header Functional Descriptor 0x10, // bcdCDC:1.1 0x01, /* ACM Functional Descriptor */ 0x04, // bFunctionLength - 0x24, // bDescriptor Type: CS_INTERFACE - 0x02, // bDescriptor Subtype: ACM Func Desc - 0x02, // bmCapabilities + 0x24, // bDescriptor Type: CS_INTERFACE + 0x02, // bDescriptor Subtype: Abstract Control Management Functional Descriptor + 0x02, // bmCapabilities: D1: Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State /* Union Functional Descriptor */ 0x05, // bFunctionLength - 0x24, // bDescriptorType: CS_INTERFACE - 0x06, // bDescriptor Subtype: Union Func Desc - 0x00, // bMasterInterface: Communication Class Interface - 0x01, // bSlaveInterface0: Data Class Interface + 0x24, // bDescriptorType: CS_INTERFACE + 0x06, // bDescriptor Subtype: Union Functional Descriptor + 0x00, // bMasterInterface: Communication Class Interface + 0x01, // bSlaveInterface0: Data Class Interface /* Call Management Functional Descriptor */ 0x05, // bFunctionLength - 0x24, // bDescriptor Type: CS_INTERFACE - 0x01, // bDescriptor Subtype: Call Management Func Desc - 0x00, // bmCapabilities: D1 + D0 - 0x01, // bDataInterface: Data Class Interface 1 + 0x24, // bDescriptor Type: CS_INTERFACE + 0x01, // bDescriptor Subtype: Call Management Functional Descriptor + 0x00, // bmCapabilities: Device sends/receives call management information only over the Communication Class interface. Device does not handle call management itself + 0x01, // bDataInterface: Data Class Interface 1 /* Endpoint 1 descriptor */ 0x07, // bLength 0x05, // bDescriptorType - 0x83, // bEndpointAddress, Endpoint 03 - IN - 0x03, // bmAttributes INT - 0x08, // wMaxPacketSize + 0x83, // bEndpointAddress: Endpoint 03 - IN + 0x03, // bmAttributes: INT + 0x08, // wMaxPacketSize: 8 0x00, 0xFF, // bInterval - /* Data Class Interface Descriptor Requirement */ + /* Interface 1 Descriptor: Data Class Interface */ 0x09, // bLength 0x04, // bDescriptorType 0x01, // bInterfaceNumber 0x00, // bAlternateSetting 0x02, // bNumEndpoints - 0x0A, // bInterfaceClass - 0x00, // bInterfaceSubclass - 0x00, // bInterfaceProtocol + 0x0A, // bInterfaceClass: Data Interface Class + 0x00, // bInterfaceSubclass: not used + 0x00, // bInterfaceProtocol: No class specific protocol required) 0x00, // iInterface - /* First alternate setting */ /* Endpoint 1 descriptor */ 0x07, // bLength 0x05, // bDescriptorType - 0x01, // bEndpointAddress, Endpoint 01 - OUT - 0x02, // bmAttributes BULK - AT91C_EP_OUT_SIZE, // wMaxPacketSize + 0x01, // bEndpointAddress: Endpoint 01 - OUT + 0x02, // bmAttributes: BULK + AT91C_EP_OUT_SIZE, // wMaxPacketSize 0x00, 0x00, // bInterval /* Endpoint 2 descriptor */ 0x07, // bLength 0x05, // bDescriptorType - 0x82, // bEndpointAddress, Endpoint 02 - IN - 0x02, // bmAttributes BULK + 0x82, // bEndpointAddress: Endpoint 02 - IN + 0x02, // bmAttributes: BULK AT91C_EP_IN_SIZE, // wMaxPacketSize 0x00, 0x00 // bInterval @@ -157,7 +162,9 @@ static const char StrDescLanguageCodes[] = { 0x03, // Type is string 0x09, 0x04 // supported language Code 0 = 0x0409 (English) }; - + +// Note: ModemManager (Linux) ignores Proxmark3 devices by matching the +// manufacturer string "proxmark.org". Don't change this. static const char StrDescManufacturer[] = { 26, // Length 0x03, // Type is string @@ -176,26 +183,30 @@ static const char StrDescManufacturer[] = { }; static const char StrDescProduct[] = { - 8, // Length + 20, // Length 0x03, // Type is string - 'P', 0x00, - 'M', 0x00, + 'p', 0x00, + 'r', 0x00, + 'o', 0x00, + 'x', 0x00, + 'm', 0x00, + 'a', 0x00, + 'r', 0x00, + 'k', 0x00, '3', 0x00 }; - -static const char* const pStrings[] = -{ - StrDescLanguageCodes, - StrDescManufacturer, - StrDescProduct -}; const char* getStringDescriptor(uint8_t idx) { - if(idx >= (sizeof(pStrings) / sizeof(pStrings[0]))) { - return(NULL); - } else { - return(pStrings[idx]); + switch (idx) { + case STR_LANGUAGE_CODES: + return StrDescLanguageCodes; + case STR_MANUFACTURER: + return StrDescManufacturer; + case STR_PRODUCT: + return StrDescProduct; + default: + return NULL; } } @@ -262,6 +273,7 @@ AT91S_CDC_LINE_CODING line = { 0, // None Parity 8}; // 8 Data bits + void AT91F_CDC_Enumerate(); AT91PS_UDP pUdp = AT91C_BASE_UDP; @@ -269,52 +281,55 @@ byte_t btConfiguration = 0; byte_t btConnection = 0; byte_t btReceiveBank = AT91C_UDP_RX_DATA_BK0; + //*---------------------------------------------------------------------------- //* \fn usb_disable //* \brief This function deactivates the USB device //*---------------------------------------------------------------------------- void usb_disable() { - // Disconnect the USB device - AT91C_BASE_PIOA->PIO_ODR = GPIO_USB_PU; - - // Clear all lingering interrupts - if(pUdp->UDP_ISR & AT91C_UDP_ENDBUSRES) { - pUdp->UDP_ICR = AT91C_UDP_ENDBUSRES; - } + // Disconnect the USB device + AT91C_BASE_PIOA->PIO_ODR = GPIO_USB_PU; + + // Clear all lingering interrupts + if(pUdp->UDP_ISR & AT91C_UDP_ENDBUSRES) { + pUdp->UDP_ICR = AT91C_UDP_ENDBUSRES; + } } + //*---------------------------------------------------------------------------- //* \fn usb_enable //* \brief This function Activates the USB device //*---------------------------------------------------------------------------- void usb_enable() { - // Set the PLL USB Divider - AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; - - // Specific Chip USB Initialisation - // Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock - AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP; - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP); - - // Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO - // Set in PIO mode and Configure in Output - AT91C_BASE_PIOA->PIO_PER = GPIO_USB_PU; // Set in PIO mode - AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; // Configure as Output - - // Clear for set the Pullup resistor - AT91C_BASE_PIOA->PIO_CODR = GPIO_USB_PU; - - // Disconnect and reconnect USB controller for 100ms - usb_disable(); - - // Wait for a short while - for (volatile size_t i=0; i<0x100000; i++); + // Set the PLL USB Divider + AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; - // Reconnect USB reconnect - AT91C_BASE_PIOA->PIO_SODR = GPIO_USB_PU; - AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; + // Specific Chip USB Initialisation + // Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock + AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP; + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP); + + // Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO + // Set in PIO mode and Configure in Output + AT91C_BASE_PIOA->PIO_PER = GPIO_USB_PU; // Set in PIO mode + AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; // Configure as Output + + // Clear for set the Pullup resistor + AT91C_BASE_PIOA->PIO_CODR = GPIO_USB_PU; + + // Disconnect and reconnect USB controller for 100ms + usb_disable(); + + // Wait for a short while + for (volatile size_t i=0; i<0x100000; i++); + + // Reconnect USB reconnect + AT91C_BASE_PIOA->PIO_SODR = GPIO_USB_PU; + AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; } + //*---------------------------------------------------------------------------- //* \fn usb_check //* \brief Test if the device is configured and handle enumeration @@ -331,8 +346,7 @@ bool usb_check() { pUdp->UDP_FADDR = AT91C_UDP_FEN; // Configure endpoint 0 pUdp->UDP_CSR[AT91C_EP_CONTROL] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL); - } - else if (isr & AT91C_UDP_EPINT0) { + } else if (isr & AT91C_UDP_EPINT0) { pUdp->UDP_ICR = AT91C_UDP_EPINT0; AT91F_CDC_Enumerate(); } @@ -342,10 +356,11 @@ bool usb_check() { bool usb_poll() { - if (!usb_check()) return false; - return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank); + if (!usb_check()) return false; + return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank); } + /** In github PR #129, some users appears to get a false positive from usb_poll, which returns true, but the usb_read operation @@ -356,7 +371,6 @@ bool usb_poll() **/ bool usb_poll_validate_length() { - if (!usb_check()) return false; if (!(pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank)) return false; return (pUdp->UDP_CSR[AT91C_EP_OUT] >> 16) > 0; @@ -393,48 +407,54 @@ uint32_t usb_read(byte_t* data, size_t len) { return nbBytesRcv; } + //*---------------------------------------------------------------------------- //* \fn usb_write //* \brief Send through endpoint 2 //*---------------------------------------------------------------------------- uint32_t usb_write(const byte_t* data, const size_t len) { - size_t length = len; + size_t length = len; uint32_t cpt = 0; - if (!length) return 0; - if (!usb_check()) return 0; - + if (!length) return 0; + if (!usb_check()) return 0; + // Send the first packet - cpt = MIN(length, AT91C_EP_IN_SIZE-1); + cpt = MIN(length, AT91C_EP_IN_SIZE); length -= cpt; - while (cpt--) pUdp->UDP_FDR[AT91C_EP_IN] = *data++; + while (cpt--) { + pUdp->UDP_FDR[AT91C_EP_IN] = *data++; + } UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); while (length) { - // Fill the second bank - cpt = MIN(length, AT91C_EP_IN_SIZE-1); + // Fill the next bank + cpt = MIN(length, AT91C_EP_IN_SIZE); length -= cpt; - while (cpt--) pUdp->UDP_FDR[AT91C_EP_IN] = *data++; - // Wait for the first bank to be sent + while (cpt--) { + pUdp->UDP_FDR[AT91C_EP_IN] = *data++; + } + // Wait for the previous bank to be sent while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) { if (!usb_check()) return length; - } + } UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP); UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); } - + // Wait for the end of transfer while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) { if (!usb_check()) return length; - } - + } + UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP); return length; } + //*---------------------------------------------------------------------------- //* \fn AT91F_USB_SendData //* \brief Send Data through the control endpoint @@ -477,6 +497,7 @@ static void AT91F_USB_SendData(AT91PS_UDP pUdp, const char *pData, uint32_t leng } } + //*---------------------------------------------------------------------------- //* \fn AT91F_USB_SendZlp //* \brief Send zero length packet through the control endpoint @@ -488,6 +509,7 @@ void AT91F_USB_SendZlp(AT91PS_UDP pUdp) { while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP); } + //*---------------------------------------------------------------------------- //* \fn AT91F_USB_SendStall //* \brief Stall the control endpoint @@ -499,6 +521,7 @@ void AT91F_USB_SendStall(AT91PS_UDP pUdp) { while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR)); } + //*---------------------------------------------------------------------------- //* \fn AT91F_CDC_Enumerate //* \brief This function is a callback invoked when a SETUP packet is received @@ -510,16 +533,16 @@ void AT91F_CDC_Enumerate() { if ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RXSETUP) ) return; - bmRequestType = pUdp->UDP_FDR[0]; - bRequest = pUdp->UDP_FDR[0]; - wValue = (pUdp->UDP_FDR[0] & 0xFF); - wValue |= (pUdp->UDP_FDR[0] << 8); - wIndex = (pUdp->UDP_FDR[0] & 0xFF); - wIndex |= (pUdp->UDP_FDR[0] << 8); - wLength = (pUdp->UDP_FDR[0] & 0xFF); - wLength |= (pUdp->UDP_FDR[0] << 8); + bmRequestType = pUdp->UDP_FDR[AT91C_EP_CONTROL]; + bRequest = pUdp->UDP_FDR[AT91C_EP_CONTROL]; + wValue = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); + wValue |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); + wIndex = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); + wIndex |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); + wLength = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); + wLength |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); - if (bmRequestType & 0x80) { + if (bmRequestType & 0x80) { // Data Phase Transfer Direction Device to Host UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_DIR); while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_DIR) ); } @@ -553,29 +576,29 @@ void AT91F_CDC_Enumerate() { btConfiguration = wValue; AT91F_USB_SendZlp(pUdp); pUdp->UDP_GLBSTATE = (wValue) ? AT91C_UDP_CONFG : AT91C_UDP_FADDEN; - pUdp->UDP_CSR[1] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) : 0; - pUdp->UDP_CSR[2] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0; - pUdp->UDP_CSR[3] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0; + pUdp->UDP_CSR[AT91C_EP_OUT] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) : 0; + pUdp->UDP_CSR[AT91C_EP_IN] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0; + pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0; break; case STD_GET_CONFIGURATION: AT91F_USB_SendData(pUdp, (char *) &(btConfiguration), sizeof(btConfiguration)); break; case STD_GET_STATUS_ZERO: - wStatus = 0; + wStatus = 0; // Device is Bus powered, remote wakeup disabled AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); break; case STD_GET_STATUS_INTERFACE: - wStatus = 0; + wStatus = 0; // reserved for future use AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); break; case STD_GET_STATUS_ENDPOINT: wStatus = 0; wIndex &= 0x0F; - if ((pUdp->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex <= 3)) { + if ((pUdp->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex <= AT91C_EP_NOTIFY)) { wStatus = (pUdp->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); } - else if ((pUdp->UDP_GLBSTATE & AT91C_UDP_FADDEN) && (wIndex == 0)) { + else if ((pUdp->UDP_GLBSTATE & AT91C_UDP_FADDEN) && (wIndex == AT91C_EP_CONTROL)) { wStatus = (pUdp->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); } @@ -590,7 +613,7 @@ void AT91F_CDC_Enumerate() { break; case STD_SET_FEATURE_ENDPOINT: wIndex &= 0x0F; - if ((wValue == 0) && wIndex && (wIndex <= 3)) { + if ((wValue == 0) && (wIndex >= AT91C_EP_OUT) && (wIndex <= AT91C_EP_NOTIFY)) { pUdp->UDP_CSR[wIndex] = 0; AT91F_USB_SendZlp(pUdp); } @@ -605,13 +628,13 @@ void AT91F_CDC_Enumerate() { break; case STD_CLEAR_FEATURE_ENDPOINT: wIndex &= 0x0F; - if ((wValue == 0) && wIndex && (wIndex <= 3)) { - if (wIndex == 1) - pUdp->UDP_CSR[1] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT); - else if (wIndex == 2) - pUdp->UDP_CSR[2] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN); - else if (wIndex == 3) - pUdp->UDP_CSR[3] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_ISO_IN); + if ((wValue == 0) && (wIndex >= AT91C_EP_OUT) && (wIndex <= AT91C_EP_NOTIFY)) { + if (wIndex == AT91C_EP_OUT) + pUdp->UDP_CSR[AT91C_EP_OUT] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT); + else if (wIndex == AT91C_EP_IN) + pUdp->UDP_CSR[AT91C_EP_IN] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN); + else if (wIndex == AT91C_EP_NOTIFY) + pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN); AT91F_USB_SendZlp(pUdp); } else diff --git a/driver/proxmark3.inf b/driver/proxmark3.inf index 122ebd55..52dbd251 100644 --- a/driver/proxmark3.inf +++ b/driver/proxmark3.inf @@ -3,16 +3,18 @@ Signature="$Windows NT$" Class=Ports ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} Provider=%ProviderName% -DriverVer=31/05/2017,1.1.0.0 +DriverVer=03/05/2018,1.1.1.0 [MANUFACTURER] %ProviderName%=DeviceList, NTx86, NTamd64 [DeviceList.NTx86] %DeviceName%=DriverInstall,USB\VID_9AC4&PID_4B8F +%DeviceName_old%=DriverInstall,USB\VID_2d2d&PID_504d [DeviceList.NTamd64] %DeviceName%=DriverInstall,USB\VID_9AC4&PID_4B8F +%DeviceName_old%=DriverInstall,USB\VID_2d2d&PID_504d [DriverInstall] include=mdmcpq.inf @@ -30,3 +32,4 @@ HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" [Strings] ProviderName = "proxmark.org" DeviceName = "Proxmark3" +DeviceName_old = "Proxmark3 (old)" diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index b9f2e629..bd296174 100644 Binary files a/fpga/fpga_hf.bit and b/fpga/fpga_hf.bit differ diff --git a/fpga/hi_iso14443a.v b/fpga/hi_iso14443a.v index ccb51d8f..b1b7b141 100644 --- a/fpga/hi_iso14443a.v +++ b/fpga/hi_iso14443a.v @@ -311,9 +311,13 @@ reg [3:0] sub_carrier_cnt; // The ARM must not send too early, otherwise the mod_sig_buf will overflow, therefore signal that we are ready // with fdt_indicator. The mod_sig_buf can buffer 29 excess data bits, i.e. a maximum delay of 29 * 16 = 464 adc_clk ticks. -// fdt_indicator could appear at ssp_din after 1 tick, the transfer needs 16 ticks, the ARM can send 128 ticks later. -// 1128 - 464 - 1 - 128 - 8 = 535 -`define FDT_INDICATOR_COUNT 11'd535 +// fdt_indicator is assigned to sendbit after at least 1 tick, the transfer to ARM needs minimum 8 ticks. Response from +// ARM could appear at ssp_dout 8 ticks later. +// 1128 - 464 - 1 - 8 - 8 = 647 +`define FDT_INDICATOR_COUNT 11'd647 +// Note: worst case, assignment to sendbit takes 15 ticks more, and transfer to ARM needs 7*16 = 112 ticks more. +// When the ARM's response then appears, the fdt_count is already 647 + 15 + 112 = 774, which still allows the ARM a possible +// response window of 1128 - 774 = 354 ticks. // reset on a pause in listen mode. I.e. the counter starts when the pause is over: assign fdt_reset = ~after_hysteresis && mod_type == `TAGSIM_LISTEN; diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index afa46c44..8233960f 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -27,22 +27,12 @@ assign pwr_hi = ck_1356megb & (~snoop); assign pwr_oe1 = 1'b0; assign pwr_oe3 = 1'b0; assign pwr_oe4 = 1'b0; +// Unused. +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; -reg [2:0] fc_div; -always @(negedge ck_1356megb) - fc_div <= fc_div + 1; +assign adc_clk = ck_1356megb; // sample frequency is 13,56 MHz -(* clock_signal = "yes" *) reg adc_clk; // sample frequency, always 16 * fc -always @(ck_1356megb, xcorr_is_848, xcorr_quarter_freq, fc_div) - if (xcorr_is_848 & ~xcorr_quarter_freq) // fc = 847.5 kHz - adc_clk <= ck_1356megb; - else if (~xcorr_is_848 & ~xcorr_quarter_freq) // fc = 424.25 kHz - adc_clk <= fc_div[0]; - else if (xcorr_is_848 & xcorr_quarter_freq) // fc = 212.125 kHz - adc_clk <= fc_div[1]; - else // fc = 106.0625 kHz - adc_clk <= fc_div[2]; - // When we're a reader, we just need to do the BPSK demod; but when we're an // eavesdropper, we also need to pick out the commands sent by the reader, // using AM. Do this the same way that we do it for the simulated tag. @@ -69,13 +59,28 @@ begin end end -// Let us report a correlation every 4 subcarrier cycles, or 4*16 samples, -// so we need a 6-bit counter. + +// Let us report a correlation every 64 samples. I.e. +// one Q/I pair after 4 subcarrier cycles for the 848kHz subcarrier, +// one Q/I pair after 2 subcarrier cycles for the 424kHz subcarriers, +// one Q/I pair for each subcarrier cyle for the 212kHz subcarrier. +// We need a 6-bit counter for the timing. reg [5:0] corr_i_cnt; -// And a couple of registers in which to accumulate the correlations. -// we would add/sub at most 32 times adc_d, the signed result can be held in 14 bits. +always @(negedge adc_clk) +begin + corr_i_cnt <= corr_i_cnt + 1; +end + +// And a couple of registers in which to accumulate the correlations. From the 64 samples +// we would add at most 32 times the difference between unmodulated and modulated signal. It should +// be safe to assume that a tag will not be able to modulate the carrier signal by more than 25%. +// 32 * 255 * 0,25 = 2040, which can be held in 11 bits. Add 1 bit for sign. +// Temporary we might need more bits. For the 212kHz subcarrier we could possible add 32 times the +// maximum signal value before a first subtraction would occur. 32 * 255 = 8160 can be held in 13 bits. +// Add one bit for sign -> need 14 bit registers but final result will fit into 12 bits. reg signed [13:0] corr_i_accum; reg signed [13:0] corr_q_accum; +// we will report maximum 8 significant bits reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; // clock and frame signal for communication to ARM @@ -83,12 +88,29 @@ reg ssp_clk; reg ssp_frame; -always @(negedge adc_clk) -begin - corr_i_cnt <= corr_i_cnt + 1; -end - +// The subcarrier reference signals +reg subcarrier_I; +reg subcarrier_Q; +always @(corr_i_cnt or xcorr_is_848 or xcorr_quarter_freq) +begin + if (xcorr_is_848 & ~xcorr_quarter_freq) // 848 kHz + begin + subcarrier_I = ~corr_i_cnt[3]; + subcarrier_Q = ~(corr_i_cnt[3] ^ corr_i_cnt[2]); + end + else if (xcorr_is_848 & xcorr_quarter_freq) // 212 kHz + begin + subcarrier_I = ~corr_i_cnt[5]; + subcarrier_Q = ~(corr_i_cnt[5] ^ corr_i_cnt[4]); + end + else + begin // 424 kHz + subcarrier_I = ~corr_i_cnt[4]; + subcarrier_Q = ~(corr_i_cnt[4] ^ corr_i_cnt[3]); + end +end + // ADC data appears on the rising edge, so sample it on the falling edge always @(negedge adc_clk) begin @@ -99,37 +121,61 @@ begin begin if(snoop) begin - // Send only 7 most significant bits of tag signal (signed), LSB is reader signal: - corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev_prev}; - corr_q_out <= {corr_q_accum[13:7], after_hysteresis_prev}; + // Send 7 most significant bits of tag signal (signed), plus 1 bit reader signal + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= {corr_i_accum[11:5], after_hysteresis_prev_prev}; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= {7'b0111111, after_hysteresis_prev_prev}; + else + corr_i_out <= {7'b1000000, after_hysteresis_prev_prev}; + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= {corr_q_accum[11:5], after_hysteresis_prev}; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= {7'b0111111, after_hysteresis_prev}; + else + corr_q_out <= {7'b1000000, after_hysteresis_prev}; after_hysteresis_prev_prev <= after_hysteresis; end else begin - // 8 most significant bits of tag signal - corr_i_out <= corr_i_accum[13:6]; - corr_q_out <= corr_q_accum[13:6]; + // Send 8 bits of tag signal + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= corr_i_accum[11:4]; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= 8'b01111111; + else + corr_i_out <= 8'b10000000; + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= corr_q_accum[11:4]; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= 8'b01111111; + else + corr_q_out <= 8'b10000000; end - - corr_i_accum <= adc_d; - corr_q_accum <= adc_d; + // Initialize next correlation. + // Both I and Q reference signals are high when corr_i_nct == 0. Therefore need to accumulate. + corr_i_accum <= $signed({1'b0,adc_d}); + corr_q_accum <= $signed({1'b0,adc_d}); end else begin - if(corr_i_cnt[3]) - corr_i_accum <= corr_i_accum - adc_d; + if (subcarrier_I) + corr_i_accum <= corr_i_accum + $signed({1'b0,adc_d}); else - corr_i_accum <= corr_i_accum + adc_d; + corr_i_accum <= corr_i_accum - $signed({1'b0,adc_d}); - if(corr_i_cnt[3] == corr_i_cnt[2]) // phase shifted by pi/2 - corr_q_accum <= corr_q_accum + adc_d; + if (subcarrier_Q) + corr_q_accum <= corr_q_accum + $signed({1'b0,adc_d}); else - corr_q_accum <= corr_q_accum - adc_d; + corr_q_accum <= corr_q_accum - $signed({1'b0,adc_d}); end - // The logic in hi_simulate.v reports 4 samples per bit. We report two - // (I, Q) pairs per bit, so we should do 2 samples per pair. + // for each Q/I pair report two reader signal samples when sniffing if(corr_i_cnt == 6'd32) after_hysteresis_prev <= after_hysteresis; @@ -151,9 +197,9 @@ begin end end - // set ssp_frame signal for corr_i_cnt = 0..3 and corr_i_cnt = 32..35 - // (send two frames with 8 Bits each) - if(corr_i_cnt[5:2] == 4'b0000 || corr_i_cnt[5:2] == 4'b1000) + // set ssp_frame signal for corr_i_cnt = 0..3 + // (send one frame with 16 Bits) + if(corr_i_cnt[5:2] == 4'b0000) ssp_frame = 1'b1; else ssp_frame = 1'b0; @@ -164,8 +210,4 @@ assign ssp_din = corr_i_out[7]; assign dbg = corr_i_cnt[3]; -// Unused. -assign pwr_lo = 1'b0; -assign pwr_oe2 = 1'b0; - endmodule diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index f12e64eb..819f1697 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -24,33 +24,36 @@ module hi_read_tx( output dbg; input shallow_modulation; +// low frequency outputs, not relevant +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; + // The high-frequency stuff. For now, for testing, just bring out the carrier, // and allow the ARM to modulate it over the SSP. reg pwr_hi; reg pwr_oe1; -reg pwr_oe2; reg pwr_oe3; reg pwr_oe4; + always @(ck_1356megb or ssp_dout or shallow_modulation) begin if(shallow_modulation) begin pwr_hi <= ck_1356megb; - pwr_oe1 <= ~ssp_dout; - pwr_oe2 <= ~ssp_dout; - pwr_oe3 <= ~ssp_dout; - pwr_oe4 <= 1'b0; + pwr_oe1 <= 1'b0; + pwr_oe3 <= 1'b0; + pwr_oe4 <= ssp_dout; end else begin - pwr_hi <= ck_1356megb & ssp_dout; + pwr_hi <= ck_1356megb & ~ssp_dout; pwr_oe1 <= 1'b0; - pwr_oe2 <= 1'b0; pwr_oe3 <= 1'b0; pwr_oe4 <= 1'b0; end end + // Then just divide the 13.56 MHz clock down to produce appropriate clocks // for the synchronous serial port. @@ -68,22 +71,8 @@ always @(negedge ssp_clk) assign ssp_frame = (hi_byte_div == 3'b000); -// Implement a hysteresis to give out the received signal on -// ssp_din. Sample at fc. -assign adc_clk = ck_1356meg; +assign ssp_din = 1'b0; -// ADC data appears on the rising edge, so sample it on the falling edge -reg after_hysteresis; -always @(negedge adc_clk) -begin - if(& adc_d[7:0]) after_hysteresis <= 1'b1; - else if(~(| adc_d[7:0])) after_hysteresis <= 1'b0; -end +assign dbg = ssp_frame; - -assign ssp_din = after_hysteresis; - -assign pwr_lo = 1'b0; -assign dbg = ssp_din; - -endmodule +endmodule \ No newline at end of file diff --git a/fpga/hi_simulate.v b/fpga/hi_simulate.v index 0768c29d..8d70bb1b 100644 --- a/fpga/hi_simulate.v +++ b/fpga/hi_simulate.v @@ -16,6 +16,13 @@ // Jonathan Westhues, October 2006 //----------------------------------------------------------------------------- +// possible mod_types: +`define NO_MODULATION 3'b000 +`define MODULATE_BPSK 3'b001 +`define MODULATE_212K 3'b010 +`define MODULATE_424K 3'b100 +`define MODULATE_424K_8BIT 3'b101 + module hi_simulate( pck0, ck_1356meg, ck_1356megb, pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, @@ -35,10 +42,6 @@ module hi_simulate( output dbg; input [2:0] mod_type; -// Power amp goes between LOW and tri-state, so pwr_hi (and pwr_lo) can -// always be low. -assign pwr_hi = 1'b0; -assign pwr_lo = 1'b0; // The comparator with hysteresis on the output from the peak detector. reg after_hysteresis; @@ -51,38 +54,29 @@ begin end -// Divide 13.56 MHz by 32 to produce the SSP_CLK -// The register is bigger to allow higher division factors of up to /128 -reg [10:0] ssp_clk_divider; +// Divide 13.56 MHz to produce various frequencies for SSP_CLK +// and modulation. +reg [7:0] ssp_clk_divider; always @(posedge adc_clk) ssp_clk_divider <= (ssp_clk_divider + 1); reg ssp_clk; -reg ssp_frame; + always @(negedge adc_clk) begin - //If we're in 101, we only need a new bit every 8th carrier bit (53Hz). Otherwise, get next bit at 424Khz - if(mod_type == 3'b101) - begin - if(ssp_clk_divider[7:0] == 8'b00000000) - ssp_clk <= 1'b0; - if(ssp_clk_divider[7:0] == 8'b10000000) - ssp_clk <= 1'b1; - - end + if(mod_type == `MODULATE_424K_8BIT) + // Get bit every at 53KHz (every 8th carrier bit of 424kHz) + ssp_clk <= ssp_clk_divider[7]; + else if(mod_type == `MODULATE_212K) + // Get next bit at 212kHz + ssp_clk <= ssp_clk_divider[5]; else - begin - if(ssp_clk_divider[4:0] == 5'd0)//[4:0] == 5'b00000) - ssp_clk <= 1'b1; - if(ssp_clk_divider[4:0] == 5'd16) //[4:0] == 5'b10000) - ssp_clk <= 1'b0; - end + // Get next bit at 424Khz + ssp_clk <= ssp_clk_divider[4]; end -//assign ssp_clk = ssp_clk_divider[4]; - // Divide SSP_CLK by 8 to produce the byte framing signal; the phase of // this is arbitrary, because it's just a bitstream. // One nasty issue, though: I can't make it work with both rx and tx at @@ -96,46 +90,45 @@ always @(negedge ssp_clk) ssp_frame_divider_from_arm <= (ssp_frame_divider_from_arm + 1); - +reg ssp_frame; always @(ssp_frame_divider_to_arm or ssp_frame_divider_from_arm or mod_type) - if(mod_type == 3'b000) // not modulating, so listening, to ARM + if(mod_type == `NO_MODULATION) // not modulating, so listening, to ARM ssp_frame = (ssp_frame_divider_to_arm == 3'b000); else - ssp_frame = (ssp_frame_divider_from_arm == 3'b000); + ssp_frame = (ssp_frame_divider_from_arm == 3'b000); // Synchronize up the after-hysteresis signal, to produce DIN. reg ssp_din; always @(posedge ssp_clk) ssp_din = after_hysteresis; -// Modulating carrier frequency is fc/16, reuse ssp_clk divider for that +// Modulating carrier frequency is fc/64 (212kHz) to fc/16 (848kHz). Reuse ssp_clk divider for that. reg modulating_carrier; always @(mod_type or ssp_clk or ssp_dout) - if(mod_type == 3'b000) + if (mod_type == `NO_MODULATION) modulating_carrier <= 1'b0; // no modulation - else if(mod_type == 3'b001) + else if (mod_type == `MODULATE_BPSK) modulating_carrier <= ssp_dout ^ ssp_clk_divider[3]; // XOR means BPSK - else if(mod_type == 3'b010) - modulating_carrier <= ssp_dout & ssp_clk_divider[5]; // switch 212kHz subcarrier on/off - else if(mod_type == 3'b100 || mod_type == 3'b101) - modulating_carrier <= ssp_dout & ssp_clk_divider[4]; // switch 424kHz modulation on/off + else if (mod_type == `MODULATE_212K) + modulating_carrier <= ssp_dout & ssp_clk_divider[5]; // switch 212kHz subcarrier on/off + else if (mod_type == `MODULATE_424K || mod_type == `MODULATE_424K_8BIT) + modulating_carrier <= ssp_dout & ssp_clk_divider[4]; // switch 424kHz modulation on/off else modulating_carrier <= 1'b0; // yet unused -// This one is all LF, so doesn't matter -assign pwr_oe2 = modulating_carrier; -// Toggle only one of these, since we are already producing much deeper +// Load modulation. Toggle only one of these, since we are already producing much deeper // modulation than a real tag would. -assign pwr_oe1 = modulating_carrier; -assign pwr_oe4 = modulating_carrier; +assign pwr_hi = 1'b0; // HF antenna connected to GND +assign pwr_oe3 = 1'b0; // 10k Load +assign pwr_oe1 = modulating_carrier; // 33 Ohms Load +assign pwr_oe4 = modulating_carrier; // 33 Ohms Load -// This one is always on, so that we can watch the carrier. -assign pwr_oe3 = 1'b0; +// This is all LF and doesn't matter +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; -assign dbg = modulating_carrier; -//reg dbg; -//always @(ssp_dout) -// dbg <= ssp_dout; + +assign dbg = ssp_din; endmodule diff --git a/include/legic.h b/include/legic.h new file mode 100644 index 00000000..246af0e8 --- /dev/null +++ b/include/legic.h @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// (c) 2016 Iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// LEGIC type prototyping +//----------------------------------------------------------------------------- + +#ifndef _LEGIC_H_ +#define _LEGIC_H_ + +#include "common.h" + +//----------------------------------------------------------------------------- +// LEGIC +//----------------------------------------------------------------------------- +typedef struct { + uint8_t uid[4]; + uint32_t tagtype; + uint8_t cmdsize; + uint8_t addrsize; + uint16_t cardsize; +} legic_card_select_t; + +#endif // _LEGIC_H_ diff --git a/include/mifare.h b/include/mifare.h index e2386cd5..b821f32b 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -34,7 +34,9 @@ typedef enum ISO14A_COMMAND { ISO14A_APPEND_CRC = (1 << 5), ISO14A_SET_TIMEOUT = (1 << 6), ISO14A_NO_SELECT = (1 << 7), - ISO14A_TOPAZMODE = (1 << 8) + ISO14A_TOPAZMODE = (1 << 8), + ISO14A_NO_RATS = (1 << 9), + ISO14A_CLEAR_TRACE = (1 << 10) } iso14a_command_t; typedef struct { diff --git a/include/proxmark3.h b/include/proxmark3.h index 4a59636e..fec7415a 100644 --- a/include/proxmark3.h +++ b/include/proxmark3.h @@ -16,73 +16,71 @@ #include "config_gpio.h" #include "usb_cmd.h" -#define WDT_HIT() AT91C_BASE_WDTC->WDTC_WDCR = 0xa5000001 +#define WDT_HIT() AT91C_BASE_WDTC->WDTC_WDCR = 0xa5000001 -#define PWM_CH_MODE_PRESCALER(x) ((x)<<0) -#define PWM_CHANNEL(x) (1<<(x)) +#define PWM_CH_MODE_PRESCALER(x) ((x)<<0) +#define PWM_CHANNEL(x) (1<<(x)) -#define ADC_CHAN_LF 4 -#define ADC_CHAN_HF 5 -#define ADC_MODE_PRESCALE(x) ((x)<<8) -#define ADC_MODE_STARTUP_TIME(x) ((x)<<16) -#define ADC_MODE_SAMPLE_HOLD_TIME(x) ((x)<<24) -#define ADC_CHANNEL(x) (1<<(x)) -#define ADC_END_OF_CONVERSION(x) (1<<(x)) +#define ADC_CHAN_LF 4 +#define ADC_CHAN_HF_LOW 5 +#define ADC_CHAN_HF_HIGH 7 +#define ADC_MODE_PRESCALE(x) ((x)<<8) +#define ADC_MODE_STARTUP_TIME(x) ((x)<<16) +#define ADC_MODE_SAMPLE_HOLD_TIME(x) ((x)<<24) +#define ADC_CHANNEL(x) (1<<(x)) +#define ADC_END_OF_CONVERSION(x) (1<<(x)) -#define SSC_CLOCK_MODE_START(x) ((x)<<8) -#define SSC_FRAME_MODE_WORDS_PER_TRANSFER(x) ((x)<<8) -#define SSC_CLOCK_MODE_SELECT(x) ((x)<<0) -#define SSC_FRAME_MODE_BITS_IN_WORD(x) (((x)-1)<<0) +#define SSC_CLOCK_MODE_START(x) ((x)<<8) +#define SSC_FRAME_MODE_WORDS_PER_TRANSFER(x) ((x)<<8) +#define SSC_CLOCK_MODE_SELECT(x) ((x)<<0) +#define SSC_FRAME_MODE_BITS_IN_WORD(x) (((x)-1)<<0) -#define MC_FLASH_COMMAND_KEY ((0x5a)<<24) -#define MC_FLASH_MODE_FLASH_WAIT_STATES(x) ((x)<<8) -#define MC_FLASH_MODE_MASTER_CLK_IN_MHZ(x) (((x)+((x)/2))<<16) -#define MC_FLASH_COMMAND_PAGEN(x) ((x)<<8) +#define MC_FLASH_COMMAND_KEY ((0x5a)<<24) +#define MC_FLASH_MODE_FLASH_WAIT_STATES(x) ((x)<<8) +#define MC_FLASH_MODE_MASTER_CLK_IN_MHZ(x) (((x)+((x)/2))<<16) +#define MC_FLASH_COMMAND_PAGEN(x) ((x)<<8) -#define RST_CONTROL_KEY (0xa5<<24) +#define RST_CONTROL_KEY (0xa5<<24) -#define PMC_MAIN_OSC_STARTUP_DELAY(x) ((x)<<8) -#define PMC_PLL_DIVISOR(x) (x) -#define PMC_PLL_MULTIPLIER(x) (((x)-1)<<16) -#define PMC_PLL_COUNT_BEFORE_LOCK(x) ((x)<<8) -#define PMC_PLL_FREQUENCY_RANGE(x) ((x)<<14) -#define PMC_PLL_USB_DIVISOR(x) ((x)<<28) +#define PMC_MAIN_OSC_STARTUP_DELAY(x) ((x)<<8) +#define PMC_PLL_DIVISOR(x) (x) +#define PMC_PLL_MULTIPLIER(x) (((x)-1)<<16) +#define PMC_PLL_COUNT_BEFORE_LOCK(x) ((x)<<8) +#define PMC_PLL_FREQUENCY_RANGE(x) ((x)<<14) +#define PMC_PLL_USB_DIVISOR(x) ((x)<<28) -#define UDP_INTERRUPT_ENDPOINT(x) (1<<(x)) -#define UDP_CSR_BYTES_RECEIVED(x) (((x) >> 16) & 0x7ff) +#define UDP_INTERRUPT_ENDPOINT(x) (1<<(x)) +#define UDP_CSR_BYTES_RECEIVED(x) (((x) >> 16) & 0x7ff) //************************************************************** -#define LOW(x) AT91C_BASE_PIOA->PIO_CODR = (x) -#define HIGH(x) AT91C_BASE_PIOA->PIO_SODR = (x) -#define GETBIT(x) (AT91C_BASE_PIOA->PIO_ODSR & (x)) ? 1:0 -#define SETBIT(x, y) (y) ? (HIGH(x)):(LOW(x)) -#define INVBIT(x) SETBIT((x), !(GETBIT(x))) +#define LOW(x) AT91C_BASE_PIOA->PIO_CODR = (x) +#define HIGH(x) AT91C_BASE_PIOA->PIO_SODR = (x) +#define GETBIT(x) (AT91C_BASE_PIOA->PIO_ODSR & (x)) ? 1:0 +#define SETBIT(x, y) (y) ? (HIGH(x)):(LOW(x)) +#define INVBIT(x) SETBIT((x), !(GETBIT(x))) -#define SPI_FPGA_MODE 0 -#define SPI_LCD_MODE 1 - -#define TRUE 1 -#define FALSE 0 +#define SPI_FPGA_MODE 0 +#define SPI_LCD_MODE 1 //#define PACKED __attribute__((__packed__)) -#define LED_A_ON() HIGH(GPIO_LED_A) -#define LED_A_OFF() LOW(GPIO_LED_A) -#define LED_A_INV() INVBIT(GPIO_LED_A) -#define LED_B_ON() HIGH(GPIO_LED_B) -#define LED_B_OFF() LOW(GPIO_LED_B) -#define LED_B_INV() INVBIT(GPIO_LED_B) -#define LED_C_ON() HIGH(GPIO_LED_C) -#define LED_C_OFF() LOW(GPIO_LED_C) -#define LED_C_INV() INVBIT(GPIO_LED_C) -#define LED_D_ON() HIGH(GPIO_LED_D) -#define LED_D_OFF() LOW(GPIO_LED_D) -#define LED_D_INV() INVBIT(GPIO_LED_D) -#define RELAY_ON() HIGH(GPIO_RELAY) -#define RELAY_OFF() LOW(GPIO_RELAY) -#define BUTTON_PRESS() !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_BUTTON) +#define LED_A_ON() HIGH(GPIO_LED_A) +#define LED_A_OFF() LOW(GPIO_LED_A) +#define LED_A_INV() INVBIT(GPIO_LED_A) +#define LED_B_ON() HIGH(GPIO_LED_B) +#define LED_B_OFF() LOW(GPIO_LED_B) +#define LED_B_INV() INVBIT(GPIO_LED_B) +#define LED_C_ON() HIGH(GPIO_LED_C) +#define LED_C_OFF() LOW(GPIO_LED_C) +#define LED_C_INV() INVBIT(GPIO_LED_C) +#define LED_D_ON() HIGH(GPIO_LED_D) +#define LED_D_OFF() LOW(GPIO_LED_D) +#define LED_D_INV() INVBIT(GPIO_LED_D) +#define RELAY_ON() HIGH(GPIO_RELAY) +#define RELAY_OFF() LOW(GPIO_RELAY) +#define BUTTON_PRESS() !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_BUTTON) -#define VERSION_INFORMATION_MAGIC 0x56334d50 +#define VERSION_INFORMATION_MAGIC 0x56334d50 struct version_information { int magic; /* Magic sequence to identify this as a correct version information structure. Must be VERSION_INFORMATION_MAGIC */ char versionversion; /* Must be 1 */ @@ -92,9 +90,9 @@ struct version_information { char buildtime[30]; /* string with the build time */ } __attribute__((packed)); -#define COMMON_AREA_MAGIC 0x43334d50 -#define COMMON_AREA_COMMAND_NONE 0 -#define COMMON_AREA_COMMAND_ENTER_FLASH_MODE 1 +#define COMMON_AREA_MAGIC 0x43334d50 +#define COMMON_AREA_COMMAND_NONE 0 +#define COMMON_AREA_COMMAND_ENTER_FLASH_MODE 1 struct common_area { int magic; /* Magic sequence, to distinguish against random uninitialized memory */ char version; /* Must be 1 */ diff --git a/include/smartcard.h b/include/smartcard.h new file mode 100644 index 00000000..9bed8c9d --- /dev/null +++ b/include/smartcard.h @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// (c) 2018 Iceman, adapted by Marshmellow +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// smart card type prototyping +//----------------------------------------------------------------------------- +#ifndef __SMARTCARD_H +#define __SMARTCARD_H + +//----------------------------------------------------------------------------- +// ISO 7618 Smart Card +//----------------------------------------------------------------------------- +typedef struct { + uint8_t atr_len; + uint8_t atr[30]; +} __attribute__((__packed__)) smart_card_atr_t; + +typedef enum SMARTCARD_COMMAND { + SC_CONNECT = (1 << 0), + SC_NO_DISCONNECT = (1 << 1), + SC_RAW = (1 << 2), + SC_NO_SELECT = (1 << 3) +} smartcard_command_t; + + +#endif diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 0ab9730e..bdff7261 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -60,8 +60,17 @@ typedef struct{ #define CMD_BUFF_CLEAR 0x0105 #define CMD_READ_MEM 0x0106 #define CMD_VERSION 0x0107 -#define CMD_STATUS 0x0108 -#define CMD_PING 0x0109 +#define CMD_STATUS 0x0108 +#define CMD_PING 0x0109 + +// RDV40, Smart card operations +#define CMD_SMART_RAW 0x0140 +#define CMD_SMART_UPGRADE 0x0141 +#define CMD_SMART_UPLOAD 0x0142 +#define CMD_SMART_ATR 0x0143 +// CMD_SMART_SETBAUD is unused for now +#define CMD_SMART_SETBAUD 0x0144 +#define CMD_SMART_SETCLOCK 0x0145 // For low-frequency tags #define CMD_READ_TI_TYPE 0x0202 @@ -126,10 +135,10 @@ typedef struct{ #define CMD_READER_HITAG 0x0372 #define CMD_SIMULATE_HITAG_S 0x0368 -#define CMD_TEST_HITAGS_TRACES 0x0367 -#define CMD_READ_HITAG_S 0x0373 -#define CMD_WR_HITAG_S 0x0375 -#define CMD_EMU_HITAG_S 0x0376 +#define CMD_TEST_HITAGS_TRACES 0x0367 +#define CMD_READ_HITAG_S 0x0373 +#define CMD_WR_HITAG_S 0x0375 +#define CMD_EMU_HITAG_S 0x0376 #define CMD_SIMULATE_TAG_ISO_14443B 0x0381 @@ -175,6 +184,7 @@ typedef struct{ #define CMD_MIFARE_CSETBLOCK 0x0605 #define CMD_MIFARE_CGETBLOCK 0x0606 #define CMD_MIFARE_CIDENT 0x0607 +#define CMD_MIFARE_CWIPE 0x0608 #define CMD_SIMULATE_MIFARE_CARD 0x0610 diff --git a/tools/at91sam7s512-busblaster.cfg b/tools/at91sam7s512-busblaster.cfg new file mode 100644 index 00000000..6555936b --- /dev/null +++ b/tools/at91sam7s512-busblaster.cfg @@ -0,0 +1,54 @@ +## General OpenOCD configuration +# Ports +telnet_port 4444 +gdb_port 3333 + +## Interface configuration section +# Interface + +# you can use +#source [find interface/ftdi/dp_busblaster.cfg] +# or + +interface ftdi +ftdi_device_desc "Dual RS232-HS" +ftdi_vid_pid 0x0403 0x6010 + +ftdi_layout_init 0x0c08 0x0f1b +ftdi_layout_signal nTRST -data 0x0100 -noe 0x0400 +ftdi_layout_signal nSRST -data 0x0200 -noe 0x0800 + +adapter_khz 1000 + +## Chipset configuration section +# use combined on interfaces or targets that can't set TRST/SRST separately +reset_config srst_only srst_pulls_trst + +jtag newtap sam7x cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id 0x3f0f0f0f + +target create sam7x.cpu arm7tdmi -endian little -chain-position sam7x.cpu + +sam7x.cpu configure -event reset-init { + soft_reset_halt + mww 0xfffffd00 0xa5000004 # RSTC_CR: Reset peripherals + mww 0xfffffd44 0x00008000 # WDT_MR: disable watchdog + mww 0xfffffd08 0xa5000001 # RSTC_MR enable user reset + mww 0xfffffc20 0x00005001 # CKGR_MOR : enable the main oscillator + sleep 10 + mww 0xfffffc2c 0x000b1c02 # CKGR_PLLR: 16MHz * 12/2 = 96MHz + sleep 10 + mww 0xfffffc30 0x00000007 # PMC_MCKR : MCK = PLL / 2 = 48 MHz + sleep 10 + mww 0xffffff60 0x00480100 # MC_FMR: flash mode (FWS=1,FMCN=72) + sleep 100 + +} + +# GDB can also flash my flash! +gdb_memory_map enable +gdb_breakpoint_override hard +#armv4_5 core_state arm + +sam7x.cpu configure -work-area-virt 0 -work-area-phys 0x00200000 -work-area-size 0x10000 -work-area-backup 0 +flash bank sam7x512.flash.0 at91sam7 0 0 0 0 sam7x.cpu 0 0 0 0 0 0 0 18432 +flash bank sam7x512.flash.1 at91sam7 0 0 0 0 sam7x.cpu 1 0 0 0 0 0 0 18432 diff --git a/tools/mfkey/Makefile b/tools/mfkey/Makefile index 92d3be72..9dab37f5 100755 --- a/tools/mfkey/Makefile +++ b/tools/mfkey/Makefile @@ -1,8 +1,8 @@ VPATH = ../../common ../../common/crapto1 ../../client CC = gcc LD = gcc -CFLAGS = -std=c99 -D_ISOC99_SOURCE -I../../include -I../../common -I../../client -Wall -O3 -LDFLAGS = +CFLAGS += -std=c99 -D_ISOC99_SOURCE -I../../include -I../../common -I../../client -Wall -O3 +LDFLAGS += OBJS = crypto1.o crapto1.o parity.o util_posix.o mfkey.o EXES = mfkey32 mfkey64 diff --git a/tools/mfkey/mfkey32.c b/tools/mfkey/mfkey32.c index a5665ab2..d84305d0 100755 --- a/tools/mfkey/mfkey32.c +++ b/tools/mfkey/mfkey32.c @@ -10,7 +10,7 @@ // 32 bit recover key from 2 nonces int main (int argc, char *argv[]) { - nonces_t data; + nonces_t data = {0}; uint32_t ks2; // keystream used to encrypt reader response uint64_t key; // recovered key diff --git a/uart/uart.h b/uart/uart.h index fe75a683..3b563be2 100644 --- a/uart/uart.h +++ b/uart/uart.h @@ -39,7 +39,15 @@ #include #include -typedef unsigned char byte_t; +/* Used to substitute for an example serial port path on each platform. + */ +#ifdef _WIN32 +#define SERIAL_PORT_H "com3" +#elif __APPLE__ +#define SERIAL_PORT_H "/dev/tty.usbmodem*" +#else +#define SERIAL_PORT_H "/dev/ttyACM0" +#endif /* serial_port is declared as a void*, which you should cast to whatever type * makes sense to your connection method. Both the posix and win32 @@ -78,13 +86,13 @@ void uart_close(const serial_port sp); * partial read may have completed into the buffer by the corresponding * implementation, so pszRxLen should be checked to see if any data was written. */ -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen); +bool uart_receive(const serial_port sp, uint8_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen); /* Sends a buffer to a given serial port. * pbtTx: A pointer to a buffer containing the data to send. * szTxLen: The amount of data to be sent. */ -bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen); +bool uart_send(const serial_port sp, const uint8_t* pbtTx, const size_t szTxLen); /* Sets the current speed of the serial port, in baud. */ diff --git a/uart/uart_posix.c b/uart/uart_posix.c index 45e0d3d2..0e7f7f47 100644 --- a/uart/uart_posix.c +++ b/uart/uart_posix.c @@ -133,7 +133,7 @@ void uart_close(const serial_port sp) { free(sp); } -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) { +bool uart_receive(const serial_port sp, uint8_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) { int res; int byteCount; fd_set rfds; @@ -192,7 +192,7 @@ bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_ return true; } -bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { +bool uart_send(const serial_port sp, const uint8_t* pbtTx, const size_t szTxLen) { int32_t res; size_t szPos = 0; fd_set rfds; diff --git a/uart/uart_win32.c b/uart/uart_win32.c index af521ead..b15e8786 100644 --- a/uart/uart_win32.c +++ b/uart/uart_win32.c @@ -107,15 +107,13 @@ void uart_close(const serial_port sp) { free(sp); } -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) { - ReadFile(((serial_port_windows*)sp)->hPort,pbtRx,pszMaxRxLen,(LPDWORD)pszRxLen,NULL); - return (*pszRxLen != 0); +bool uart_receive(const serial_port sp, uint8_t *pbtRx, size_t pszMaxRxLen, size_t *pszRxLen) { + return ReadFile(((serial_port_windows*)sp)->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); } -bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { +bool uart_send(const serial_port sp, const uint8_t* pbtTx, const size_t szTxLen) { DWORD dwTxLen = 0; - return WriteFile(((serial_port_windows*)sp)->hPort,pbtTx,szTxLen,&dwTxLen,NULL); - return (dwTxLen != 0); + return WriteFile(((serial_port_windows*)sp)->hPort, pbtTx, szTxLen, &dwTxLen, NULL); } bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) {