├── .gitignore ├── LICENSE ├── README.md ├── corelib ├── IDevice.h ├── corelib.vcxproj ├── corelib.vcxproj.filters ├── emvl2AppSelection.cpp ├── emvl2AppSelection.h ├── emvl2Command.cpp ├── emvl2Command.h ├── emvl2Definitions.cpp ├── emvl2Defs.h ├── emvl2GPO.cpp ├── emvl2GPO.h ├── emvl2GenAC1.cpp ├── emvl2GenAC1.h ├── emvl2GenAC2.cpp ├── emvl2GenAC2.h ├── emvl2Impl.cpp ├── emvl2Impl.h ├── emvl2OfflineDataAuth.cpp ├── emvl2OfflineDataAuth.h ├── emvl2ProcessCVM.cpp ├── emvl2ProcessCVM.h ├── emvl2ProcessRestrict.cpp ├── emvl2ProcessRestrict.h ├── emvl2ReadAppData.cpp ├── emvl2ReadAppData.h ├── emvl2Repo.cpp ├── emvl2Repo.h ├── emvl2SecUtil.cpp ├── emvl2SecUtil.h ├── emvl2TagDef.h ├── emvl2TermActionAnalysis.cpp ├── emvl2TermActionAnalysis.h ├── emvl2TerminalRiskMng.cpp ├── emvl2TerminalRiskMng.h ├── emvl2Util.cpp ├── emvl2Util.h ├── entrypoint.cpp ├── entrypoint.h └── l1 │ ├── bigdigits.c │ ├── bigdigits.h │ ├── bigdtypes.h │ ├── deviceImpl.cpp │ ├── deviceImpl.h │ ├── sha1.c │ └── sha1.h ├── kernel.cpp ├── kernel.sln ├── kernel.vcxproj └── kernel.vcxproj.filters /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # EMVKernel 4 | 5 | ## Overview 6 | A complete, robust, and efficient open-source implementation of the EMV contact kernel protocol based on EMV 4.3 specifications, written in C++. 7 | 8 | ## Features 9 | - **Comprehensive EMV Protocol Support**: Fully supports EMV contact transactions, including all key steps from application selection to transaction completion. 10 | - **Modular Design**: Organized in a modular structure to facilitate understanding, maintenance, and extensions. 11 | - **High Performance**: Optimized for performance to ensure quick transaction processing. 12 | - **Error Handling**: Includes detailed error handling and logging mechanisms to aid in debugging and ensure reliability. 13 | - **Open Source**: Released under the Apache 2.0 license, encouraging contributions and collaborative development. 14 | 15 | ## Getting Started 16 | 17 | ### Prerequisites 18 | - Visual Studio (2019 or later) 19 | 20 | ### Clone the Repository 21 | Open a terminal and run: 22 | ```sh 23 | git clone https://github.com/thearistotlemethod/EMVKernel.git 24 | ``` 25 | 26 | ### Open in Visual Studio 27 | 1. Launch Visual Studio. 28 | 2. Select **Open a project or solution**. 29 | 3. Navigate to the cloned repository directory and select the `EMVKernel.sln` file. 30 | 4. Build the solution by selecting **Build > Build Solution**. 31 | 32 | ### Running the Project 33 | 1. Set the startup project: 34 | - Right-click on the desired project in the Solution Explorer. 35 | - Select **Set as Startup Project**. 36 | 2. Run the project by pressing **F5** or selecting **Debug > Start Debugging**. 37 | 38 | ## Contributing 39 | Contributions are welcome! If you have suggestions for improvements or want to report bugs, please open an issue. For major changes, please fork the repository and submit a pull request. 40 | 41 | ## License 42 | This project is licensed under the Apache 2.0 License. See the `LICENSE` file for more details. 43 | 44 | ## Acknowledgements 45 | Special thanks to all contributors and the open-source community for their support and contributions. 46 | -------------------------------------------------------------------------------- /corelib/IDevice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | 4 | class IDevice 5 | { 6 | public: 7 | virtual emvl2Ret cardReset() = 0; 8 | virtual emvl2Ret cardSendReceive(uint8_t* sendData, uint32_t sendDataLength, uint8_t* replyData, uint32_t* replyDataLength) = 0; 9 | virtual uint8_t cardProtocol() = 0; 10 | virtual emvl2Ret pinOfflinePlain(uint8_t *cardSws) = 0; 11 | virtual emvl2Ret pinOfflineEncrypted(uint8_t* publicKeyMod, uint8_t publicKeyModLength, uint8_t* publicKeyExponent, uint8_t publicKeyExponentLength, uint8_t unpredictNumber[8], uint8_t *cardSws) = 0; 12 | virtual emvl2Ret pinOnline(uint8_t* pan, uint8_t len) = 0; 13 | virtual emvl2Ret getDateTime(emvl2DateTime* datetime) = 0; 14 | virtual void logf(const char* format, ...) = 0; 15 | virtual void hexdump(void* ptr, int buflen) = 0; 16 | virtual emvl2Ret sha1(uint8_t* data, uint32_t length, uint8_t digest[20]) = 0; 17 | virtual emvl2Ret rsaDecrypt(uint8_t* modulus, uint8_t modulusLength, uint8_t* exponent, uint8_t exponentLength, uint8_t* inputData, uint8_t inputDataLength, uint8_t* decryptedData) = 0; 18 | virtual emvl2Ret genRand(uint8_t* unpredictNumber, int len) = 0; 19 | }; -------------------------------------------------------------------------------- /corelib/corelib.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 16.0 61 | Win32Proj 62 | {2c6cbd71-80f4-43bb-af55-a5142dc165c2} 63 | corelib 64 | 10.0 65 | 66 | 67 | 68 | DynamicLibrary 69 | true 70 | v143 71 | Unicode 72 | 73 | 74 | DynamicLibrary 75 | false 76 | v143 77 | true 78 | Unicode 79 | 80 | 81 | DynamicLibrary 82 | true 83 | v143 84 | Unicode 85 | 86 | 87 | DynamicLibrary 88 | false 89 | v143 90 | true 91 | Unicode 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Level3 114 | false 115 | WIN32;_DEBUG;EMVL2EXPORT_SYMBOLS;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 116 | true 117 | NotUsing 118 | pch.h 119 | stdcpp17 120 | 121 | 122 | Windows 123 | true 124 | false 125 | 126 | 127 | copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)..\EMVLogger\lib\$(TargetName).dll" 128 | 129 | 130 | 131 | 132 | Level3 133 | true 134 | true 135 | true 136 | WIN32;EMVL2EXPORT_SYMBOLS;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 137 | true 138 | NotUsing 139 | pch.h 140 | stdcpp17 141 | 142 | 143 | Windows 144 | true 145 | true 146 | true 147 | false 148 | UseLinkTimeCodeGeneration 149 | 150 | 151 | copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)..\kerneltool\lib\$(TargetName).dll" 152 | 153 | 154 | 155 | 156 | Level3 157 | false 158 | WIN32;_DEBUG;EMVL2EXPORT_SYMBOLS;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 159 | true 160 | NotUsing 161 | pch.h 162 | stdcpp17 163 | 164 | 165 | Windows 166 | true 167 | false 168 | 169 | 170 | 171 | 172 | Level3 173 | true 174 | true 175 | true 176 | NDEBUG;CORELIB_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 177 | true 178 | Use 179 | pch.h 180 | 181 | 182 | Windows 183 | true 184 | true 185 | true 186 | false 187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /corelib/corelib.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8648c6ad-db43-459d-8d76-186453f5112a} 18 | 19 | 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | Source Files 62 | 63 | 64 | Source Files 65 | 66 | 67 | Source Files 68 | 69 | 70 | l1 71 | 72 | 73 | l1 74 | 75 | 76 | l1 77 | 78 | 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | Header Files 97 | 98 | 99 | Header Files 100 | 101 | 102 | Header Files 103 | 104 | 105 | Header Files 106 | 107 | 108 | Header Files 109 | 110 | 111 | Header Files 112 | 113 | 114 | Header Files 115 | 116 | 117 | Header Files 118 | 119 | 120 | Header Files 121 | 122 | 123 | Header Files 124 | 125 | 126 | Header Files 127 | 128 | 129 | Header Files 130 | 131 | 132 | Header Files 133 | 134 | 135 | l1 136 | 137 | 138 | l1 139 | 140 | 141 | l1 142 | 143 | 144 | l1 145 | 146 | 147 | Header Files 148 | 149 | 150 | -------------------------------------------------------------------------------- /corelib/emvl2AppSelection.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2AppSelection.h" 2 | 3 | #define PSERESPONSE 0 4 | #define ADFRESPONSE 2 5 | 6 | EmvL2AppSelection::EmvL2AppSelection(IDevice& device) : device(device),repo(EmvL2Repo::getInstance()), command(EmvL2Command::getInstance()), util(EmvL2Util::getInstance()) { 7 | } 8 | 9 | EmvL2AppSelection::~EmvL2AppSelection() { 10 | 11 | } 12 | 13 | uint8_t EmvL2AppSelection::perform(){ 14 | vector aidlist; 15 | 16 | for(const auto prm: repo.aidPrms){ 17 | if (prm.aid[0] == 0x00) { 18 | repo.parseTags(prm.data, prm.len); 19 | } 20 | else { 21 | char aidStr[32] = {0}; 22 | util.bcd2Str(prm.aid, prm.aidLen, (uint8_t *)aidStr); 23 | aidlist.push_back(string(aidStr)); 24 | } 25 | } 26 | 27 | int rv = applyPseSelection(aidlist); 28 | if (candList.size() == 0) 29 | { 30 | rv = applyLstSelection(aidlist); 31 | if (rv != success) 32 | { 33 | return rv; 34 | } 35 | 36 | if (candList.size() == 0) 37 | { 38 | return noMatchingApp; 39 | } 40 | } 41 | 42 | return finalSelect(0); 43 | } 44 | 45 | uint8_t EmvL2AppSelection::finalSelect(int idx) 46 | { 47 | repo.clearCardTags(); 48 | 49 | for (size_t i = 0; i < repo.aidPrms.size(); i++) { 50 | emvl2AIDPrms prm = repo.aidPrms[i]; 51 | if (prm.aid[0] == 0x00) { 52 | repo.parseTags(prm.data, prm.len); 53 | } 54 | else if(!memcmp(prm.aid, candList[idx]->tag9F06.val.data(), prm.aidLen)) { 55 | repo.parseTags(prm.data, prm.len); 56 | break; 57 | } 58 | } 59 | 60 | int rv = command.select(candList[idx]->tag9F06.val.data(), candList[idx]->tag9F06.len); 61 | if (rv != success) 62 | { 63 | return rv; 64 | } 65 | 66 | uint8_t sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 67 | uint8_t sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 68 | 69 | if ((sw1 == 0x90) && (sw2 == 0x00)) 70 | { 71 | uint8_t lenLen = 0; 72 | int len = repo.parseLen(&repo.apdu.rdata[1], &lenLen); 73 | rv = repo.parseTags(&repo.apdu.rdata[1 + lenLen], len); 74 | if (rv != success) 75 | { 76 | return rv; 77 | } 78 | 79 | repo.setTag(0x9F06, candList[idx]->tag9F06.val.data(), candList[idx]->tag9F06.len); 80 | return success; 81 | } 82 | 83 | return failure; 84 | } 85 | 86 | uint8_t EmvL2AppSelection::applyPseSelection(vector aidlist) 87 | { 88 | const string pseDfName = "1PAY.SYS.DDF01"; 89 | uint8_t rv = failure; 90 | 91 | candList.clear(); 92 | repo.clearCardTags(); 93 | rv = this->command.select((uint8_t *)pseDfName.data(), (uint8_t)pseDfName.length()); 94 | if (rv != success) 95 | { 96 | return rv; 97 | } 98 | 99 | uint8_t lenLen = 0; 100 | int len = repo.parseLen(&repo.apdu.rdata[1], &lenLen); 101 | rv = repo.parseTags(&repo.apdu.rdata[1 + lenLen], len); 102 | if (rv != success) 103 | { 104 | return rv; 105 | } 106 | 107 | int sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 108 | int sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 109 | 110 | if ((sw1 != 0x90) || (sw2 != 0x00)) 111 | { 112 | if ((sw1 == 0x6A) && (sw2 == 0x81)) 113 | { 114 | return cardRejected; 115 | } 116 | return pseNotSupportedByCard; 117 | } 118 | 119 | uint8_t sfi = repo.getTag(0x88)->val.data()[0]; 120 | if ((sfi > 10) || (sfi == 0)) 121 | { 122 | return pseNotSupportedByCard; 123 | } 124 | 125 | int ucRecordNo = 0; 126 | while (true) { 127 | ucRecordNo++; 128 | rv = this->command.readRecord(sfi << 3, ucRecordNo); 129 | if (rv != success) 130 | { 131 | return rv; 132 | } 133 | 134 | sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 135 | sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 136 | 137 | if ((sw1 == 0x6A) && (sw2 == 0x83)) 138 | { 139 | if (ucRecordNo == 1) 140 | { 141 | return pseNotSupportedByCard; 142 | } 143 | 144 | return success; 145 | } 146 | else if ((sw1 == 0x00) && (sw2 == 0x00)) 147 | { 148 | return cardRejected; 149 | } 150 | else if ((sw1 != 0x90) || (sw2 != 0x00)) 151 | { 152 | return pseNotSupportedByCard; 153 | } 154 | 155 | rv = readPseDir(aidlist, repo.apdu.rdata, repo.apdu.rlen - 2); 156 | if (rv != success) 157 | return rv; 158 | } 159 | } 160 | 161 | uint8_t EmvL2AppSelection::readPseDir(vector aidlist, uint8_t* data, int dataLen) { 162 | int idx = 0, rv = 0, offset = 0; 163 | uint8_t lenLen = 0; 164 | uint32_t len = 0; 165 | 166 | while (true) { 167 | if (data[idx++] != 0x70) 168 | { 169 | return emvDataFormatError; 170 | } 171 | 172 | len = repo.parseLen(&data[idx], &lenLen); 173 | idx += lenLen; 174 | 175 | if ((len + idx) > (uint32_t)dataLen) 176 | { 177 | return emvDataFormatError; 178 | } 179 | 180 | if (len == 0) 181 | { 182 | return emvDataFormatError; 183 | } 184 | 185 | if (data[idx] != 0x61) 186 | { 187 | while ((idx < dataLen) && (data[idx] != 0x61)) 188 | { 189 | repo.nextTag(data, &idx); 190 | } 191 | 192 | if (idx == dataLen) 193 | { 194 | return success; 195 | } 196 | 197 | if (idx >= dataLen) 198 | { 199 | return emvDataFormatError; 200 | } 201 | } 202 | 203 | idx++; 204 | 205 | len = repo.parseLen(&data[idx], &lenLen); 206 | idx += lenLen; 207 | offset = idx; 208 | int tmpIdx = idx; 209 | 210 | while ((idx < dataLen) && (data[idx] != 0x9D) && (data[idx] != 0x4F)) 211 | { 212 | repo.nextTag(data, &idx); 213 | } 214 | 215 | if (idx >= dataLen || (data[idx] == 0x4F && idx >= (int)(offset + len))) 216 | { 217 | return emvDataFormatError; 218 | } 219 | 220 | if (data[idx] == 0x9D) 221 | { 222 | return emvDataFormatError; 223 | } 224 | else 225 | { 226 | repo.clearCardTags(); 227 | 228 | idx = tmpIdx; 229 | rv = repo.parseTags(&data[idx], len); 230 | if (rv != success) 231 | { 232 | return pseNotSupportedByCard; 233 | } 234 | 235 | if (!repo.isTagExist(0x50)) 236 | { 237 | return emvMissingMandatoryDataError; 238 | } 239 | 240 | for (const auto aidStr : aidlist) 241 | { 242 | uint8_t aid[32]; 243 | int aidLen = 0; 244 | aidLen = aidStr.length() / 2; 245 | memcpy(aid, util.hexStr2ByteArray(aidStr.data()), aidLen); 246 | 247 | if (memcmp(repo.getTag(0x4F)->val.data(), aid, aidLen) == 0) 248 | { 249 | addToList(); 250 | break; 251 | } 252 | } 253 | 254 | idx += len; 255 | 256 | if (idx == dataLen) 257 | { 258 | return success; 259 | } 260 | else if (idx > dataLen) 261 | { 262 | return emvDataFormatError; 263 | } 264 | } 265 | } 266 | } 267 | 268 | uint8_t EmvL2AppSelection::applyLstSelection(vector aidlist) 269 | { 270 | uint8_t aid[32] = {0}, rv = failure; 271 | 272 | candList.clear(); 273 | 274 | for(const auto aidStr: aidlist) 275 | { 276 | repo.clearCardTags(); 277 | memset(aid, 0, sizeof(aid)); 278 | int aidLen = aidStr.length() / 2; 279 | memcpy(aid, util.hexStr2ByteArray(aidStr.data()), aidLen); 280 | 281 | rv = this->command.select(aid, aidLen); 282 | if (rv != success) 283 | { 284 | return rv; 285 | } 286 | 287 | uint8_t lenLen = 0; 288 | int len = repo.parseLen(&repo.apdu.rdata[1], &lenLen); 289 | rv = repo.parseTags(&repo.apdu.rdata[1 + lenLen], len); 290 | if (rv != success) 291 | { 292 | return rv; 293 | } 294 | 295 | uint8_t sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 296 | uint8_t sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 297 | 298 | if ((sw1 == 0x6A) && (sw2 == 0x81)) 299 | { 300 | if (candList[0]->tag84.len == 0) 301 | { 302 | return cardBlocked; 303 | } 304 | } 305 | else if ((sw1 == 0x90) && (sw2 == 0x00)) 306 | { 307 | addToList(aid, aidLen); 308 | } 309 | } 310 | 311 | return success; 312 | } 313 | 314 | uint8_t EmvL2AppSelection::addToList(uint8_t* aid, int aidLen) 315 | { 316 | emvl2AidInfo* cand = (emvl2AidInfo*)malloc(sizeof(emvl2AidInfo)); 317 | if (cand == NULL) { 318 | return failure; 319 | } 320 | candList.push_back(cand); 321 | memset(cand, 0, sizeof(emvl2AidInfo)); 322 | 323 | repo.setTag(0x9F06, aid, aidLen); 324 | 325 | if (repo.getTag(0x9F06)) { 326 | cand->tag9F06.len = repo.getTag(0x9F06)->len; 327 | cand->tag9F06.val = repo.getTag(0x9F06)->val; 328 | } 329 | 330 | if (repo.getTag(0x84)) { 331 | cand->tag84.len = repo.getTag(0x84)->len; 332 | cand->tag84.val = repo.getTag(0x84)->val; 333 | } 334 | 335 | if (repo.getTag(0x4F)) { 336 | cand->tag4F.len = repo.getTag(0x4F)->len; 337 | cand->tag4F.val = repo.getTag(0x4F)->val; 338 | } 339 | 340 | if (repo.getTag(0x50)) { 341 | cand->tag50.len = repo.getTag(0x50)->len; 342 | cand->tag50.val = repo.getTag(0x50)->val; 343 | } 344 | 345 | if (repo.getTag(0x87)) { 346 | cand->tag87.len = repo.getTag(0x87)->len; 347 | cand->tag87.val = repo.getTag(0x87)->val; 348 | } 349 | 350 | if (repo.getTag(0x9F38)) { 351 | cand->tag9F38.len = repo.getTag(0x9F38)->len; 352 | cand->tag9F38.val = repo.getTag(0x9F38)->val; 353 | } 354 | 355 | if (repo.getTag(0x5F2D)) { 356 | cand->tag5F2D.len = repo.getTag(0x5F2D)->len; 357 | cand->tag5F2D.val = repo.getTag(0x5F2D)->val; 358 | } 359 | 360 | if (repo.getTag(0x9F11)) { 361 | cand->tag9F11.len = repo.getTag(0x9F11)->len; 362 | cand->tag9F11.val = repo.getTag(0x9F11)->val; 363 | } 364 | 365 | if (repo.getTag(0x9F12)) { 366 | cand->tag9F12.len = repo.getTag(0x9F12)->len; 367 | cand->tag9F12.val = repo.getTag(0x9F12)->val; 368 | } 369 | 370 | if (repo.getTag(0xBF0C)) { 371 | cand->tagBF0C.len = repo.getTag(0xBF0C)->len; 372 | cand->tagBF0C.val = repo.getTag(0xBF0C)->val; 373 | } 374 | 375 | if (repo.getTag(0x5F55)) { 376 | cand->tag5F55.len = repo.getTag(0x5F55)->len; 377 | cand->tag5F55.val = repo.getTag(0x5F55)->val; 378 | } 379 | 380 | if (repo.getTag(0x42)) { 381 | cand->tag42.len = repo.getTag(0x42)->len; 382 | cand->tag42.val = repo.getTag(0x42)->val; 383 | } 384 | 385 | return success; 386 | } 387 | 388 | uint8_t EmvL2AppSelection::addToList() 389 | { 390 | Tlv* tag84 = repo.getTag(0x84); 391 | if (tag84) { 392 | return addToList(tag84->val.data(), tag84->len); 393 | } 394 | 395 | Tlv* tag4F = repo.getTag(0x4F); 396 | if (tag4F) { 397 | return addToList(tag4F->val.data(), tag4F->len); 398 | } 399 | 400 | return failure; 401 | } 402 | 403 | 404 | 405 | 406 | -------------------------------------------------------------------------------- /corelib/emvl2AppSelection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Util.h" 6 | #include "emvl2Command.h" 7 | 8 | using namespace std; 9 | 10 | class EmvL2AppSelection { 11 | public: 12 | EmvL2AppSelection(IDevice& device); 13 | ~EmvL2AppSelection(); 14 | uint8_t perform(); 15 | 16 | private: 17 | uint8_t finalSelect(int idx); 18 | uint8_t applyPseSelection(vector aidlist); 19 | uint8_t applyLstSelection(vector aidlist); 20 | uint8_t addToList(); 21 | uint8_t addToList(uint8_t* aid, int aidLen); 22 | uint8_t readPseDir(vector aidlist, uint8_t* data, int dataLen); 23 | 24 | private: 25 | IDevice& device; 26 | EmvL2Repo& repo; 27 | EmvL2Command& command; 28 | EmvL2Util& util; 29 | vector candList; 30 | }; -------------------------------------------------------------------------------- /corelib/emvl2Command.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2Command.h" 2 | 3 | EmvL2Command* EmvL2Command::instance = NULL; 4 | 5 | EmvL2Command& EmvL2Command::getInstance() { 6 | if (!instance) { 7 | instance = new EmvL2Command(); 8 | } 9 | return *instance; 10 | } 11 | 12 | EmvL2Command::EmvL2Command() : repo(EmvL2Repo::getInstance()) { 13 | device = NULL; 14 | } 15 | 16 | EmvL2Command::~EmvL2Command() { 17 | 18 | } 19 | 20 | emvl2Ret EmvL2Command::init(IDevice* device) { 21 | this->device = device; 22 | 23 | return success; 24 | } 25 | 26 | emvl2Ret EmvL2Command::generateAC(uint8_t* data, uint8_t len, uint8_t cid) { 27 | repo.clearApdu(); 28 | 29 | repo.apdu.CLA = 0x80; 30 | repo.apdu.INS = 0xAE; 31 | repo.apdu.P1 = cid; 32 | repo.apdu.P2 = 0x00; 33 | repo.apdu.Lc = len; 34 | repo.apdu.forceData = false; 35 | 36 | if (len == 0xFF) 37 | { 38 | repo.apdu.forceData = true; 39 | } 40 | 41 | repo.apdu.sdata = data; 42 | repo.apdu.Le = 0x00; 43 | 44 | emvl2Ret rv = performCommand4(&repo.apdu); 45 | return rv; 46 | } 47 | 48 | emvl2Ret EmvL2Command::getProcessingOptions(uint8_t* data, uint8_t len) { 49 | repo.clearApdu(); 50 | 51 | repo.apdu.CLA = 0x80; 52 | repo.apdu.INS = 0xA8; 53 | repo.apdu.P1 = 0x00; 54 | repo.apdu.P2 = 0x00; 55 | repo.apdu.Lc = len; 56 | repo.apdu.forceData = false; 57 | 58 | if (len == 0xFF) 59 | { 60 | repo.apdu.forceData = true; 61 | } 62 | 63 | repo.apdu.sdata = data; 64 | repo.apdu.Le = 0x00; 65 | 66 | emvl2Ret rv = performCommand4(&repo.apdu); 67 | return rv; 68 | } 69 | 70 | emvl2Ret EmvL2Command::select(uint8_t* data, uint8_t len, uint8_t p2) { 71 | repo.clearApdu(); 72 | 73 | repo.apdu.CLA = 0x00; 74 | repo.apdu.INS = 0xA4; 75 | repo.apdu.P1 = 0x04; 76 | repo.apdu.P2 = p2; 77 | repo.apdu.forceData = false; 78 | 79 | repo.apdu.Lc = len; 80 | repo.apdu.Le = 0x00; 81 | repo.apdu.sdata = data; 82 | 83 | return performCommand4(&repo.apdu); 84 | } 85 | 86 | emvl2Ret EmvL2Command::readRecord(uint8_t sfi, uint8_t idx) { 87 | repo.clearApdu(); 88 | 89 | repo.apdu.CLA = 0x00; 90 | repo.apdu.INS = 0xB2; 91 | repo.apdu.P1 = idx; 92 | repo.apdu.P2 = sfi + 4; 93 | repo.apdu.Lc = 0xFF; 94 | repo.apdu.Le = 0x00; 95 | repo.apdu.forceData = false; 96 | 97 | return performCommand2(&repo.apdu); 98 | } 99 | 100 | emvl2Ret EmvL2Command::internalAuthenticate(uint8_t* data, uint8_t len) { 101 | repo.clearApdu(); 102 | 103 | repo.apdu.CLA = 0x00; 104 | repo.apdu.INS = 0x88; 105 | repo.apdu.P1 = 0x00; 106 | repo.apdu.P2 = 0x00; 107 | repo.apdu.Lc = len; 108 | repo.apdu.forceData = false; 109 | 110 | if (len == 0xFF) 111 | { 112 | repo.apdu.forceData = true; 113 | } 114 | 115 | repo.apdu.sdata = data; 116 | repo.apdu.Le = 0x00; 117 | emvl2Ret rv = performCommand4(&repo.apdu); 118 | return rv; 119 | } 120 | 121 | emvl2Ret EmvL2Command::externalAuthenticate() { 122 | repo.clearApdu(); 123 | 124 | repo.apdu.CLA = 0x00; 125 | repo.apdu.INS = 0x82; 126 | repo.apdu.P1 = 0x00; 127 | repo.apdu.P2 = 0x00; 128 | 129 | Tlv* tag91 = repo.getTag(0x91); 130 | 131 | repo.apdu.Lc = tag91->len; 132 | repo.apdu.sdata = tag91->val.data(); 133 | 134 | repo.apdu.Le = 0xFF; 135 | repo.apdu.forceData = false; 136 | 137 | return performCommand3(&repo.apdu); 138 | } 139 | 140 | emvl2Ret EmvL2Command::getData(uint32_t tag) { 141 | repo.clearApdu(); 142 | 143 | uint8_t* data = (uint8_t *) & tag; 144 | 145 | repo.apdu.CLA = 0x80; 146 | repo.apdu.INS = 0xCA; 147 | repo.apdu.P1 = data[1]; 148 | repo.apdu.P2 = data[0]; 149 | repo.apdu.Lc = 0xFF; 150 | repo.apdu.Le = 0x00; 151 | repo.apdu.forceData = false; 152 | 153 | return performCommand2(&repo.apdu); 154 | } 155 | 156 | emvl2Ret EmvL2Command::verify(uint8_t type, uint8_t* data, uint8_t len) { 157 | repo.clearApdu(); 158 | 159 | repo.apdu.CLA = 0x00; 160 | repo.apdu.INS = 0x20; 161 | repo.apdu.P1 = 0x00; 162 | repo.apdu.P2 = type; 163 | repo.apdu.Lc = len; 164 | repo.apdu.Le = 0xFF; 165 | repo.apdu.sdata = data; 166 | repo.apdu.forceData = false; 167 | 168 | return performCommand3(&repo.apdu); 169 | } 170 | 171 | emvl2Ret EmvL2Command::getChallenge() { 172 | repo.clearApdu(); 173 | 174 | repo.apdu.CLA = 0x00; 175 | repo.apdu.INS = 0x84; 176 | repo.apdu.P1 = 0x00; 177 | repo.apdu.P2 = 0x00; 178 | repo.apdu.Lc = 0xFF; 179 | repo.apdu.Le = 0x00; 180 | repo.apdu.forceData = false; 181 | 182 | return performCommand2(&repo.apdu); 183 | } 184 | 185 | emvl2Ret EmvL2Command::performCommand1(emvl2Apdu* apdu) { 186 | return transmit(apdu); 187 | } 188 | 189 | emvl2Ret EmvL2Command::performCommand2(emvl2Apdu* apdu) { 190 | int index = 0; 191 | uint8_t readData[APDUBUFFERLEN]; 192 | int readDataLen = 0; 193 | uint8_t i = 0; 194 | 195 | while (i <= 15) 196 | { 197 | if (transmit(apdu) != success) 198 | { 199 | return cardCommError; 200 | } 201 | 202 | if ((apdu->SW1 == 0x6C)) 203 | { 204 | memcpy(&readData[index], apdu->rdata, apdu->rlen); 205 | index += apdu->rlen; 206 | readDataLen += apdu->rlen; 207 | apdu->Lc = 0xFF; 208 | apdu->Le = apdu->SW2; 209 | } 210 | else if ((apdu->SW1 == 0x61)) 211 | { 212 | memcpy(&readData[index], apdu->rdata, apdu->rlen); 213 | index += apdu->rlen; 214 | readDataLen += apdu->rlen; 215 | apdu->CLA = 0x00; 216 | apdu->INS = 0xC0; 217 | apdu->P1 = 0x00; 218 | apdu->P2 = 0x00; 219 | apdu->Lc = 0xFF; 220 | apdu->Le = apdu->SW2; 221 | } 222 | else 223 | { 224 | return success; 225 | } 226 | i++; 227 | } 228 | 229 | return cardCommError; 230 | } 231 | 232 | emvl2Ret EmvL2Command::performCommand3(emvl2Apdu* apdu) { 233 | uint8_t i = 0; 234 | 235 | while (i <= 15) 236 | { 237 | if (transmit(apdu) != success) 238 | { 239 | return cardCommError; 240 | } 241 | 242 | if ((apdu->SW1 == 0x90) && (apdu->SW2 == 0x00)) 243 | { 244 | return success; 245 | } 246 | else if (apdu->rlen - 2 == 1) 247 | { 248 | apdu->CLA = 0xFF; 249 | apdu->INS = 0xFF; 250 | } 251 | else 252 | { 253 | return success; 254 | } 255 | i++; 256 | } 257 | 258 | return cardCommError; 259 | } 260 | 261 | emvl2Ret EmvL2Command::performCommand4(emvl2Apdu* apdu) { 262 | int index = 0; 263 | uint8_t readData[APDUBUFFERLEN]; 264 | int readDataLen = 0; 265 | uint8_t i = 0; 266 | 267 | while (i <= 15) 268 | { 269 | if (transmit(apdu) != success) 270 | { 271 | return cardCommError; 272 | } 273 | if ((apdu->SW1 == 0x90) && (apdu->SW2 == 0x00)) 274 | { 275 | return success; 276 | } 277 | else if ((apdu->SW1 == 0x6C)) 278 | { 279 | memcpy(&readData[index], apdu->rdata, apdu->rlen); 280 | index += apdu->rlen; 281 | readDataLen += apdu->rlen; 282 | apdu->Lc = 0xFF; 283 | apdu->Le = apdu->SW2; 284 | } 285 | else if ((apdu->SW1 == 0x61)) 286 | { 287 | memcpy(&readData[index], apdu->rdata, apdu->rlen); 288 | index += apdu->rlen; 289 | readDataLen += apdu->rlen; 290 | apdu->CLA = 0x00; 291 | apdu->INS = 0xC0; 292 | apdu->P1 = 0x00; 293 | apdu->P2 = 0x00; 294 | apdu->Lc = 0xFF; 295 | apdu->Le = apdu->SW2; 296 | } 297 | else if ((apdu->SW1 == 0x62) || (apdu->SW1 == 0x63)) 298 | { 299 | return success; 300 | } 301 | else if (apdu->rlen - 2 == 1) 302 | { 303 | apdu->CLA = 0xFF; 304 | apdu->INS = 0xFF; 305 | } 306 | else 307 | { 308 | return success; 309 | } 310 | i++; 311 | } 312 | 313 | return cardCommError; 314 | } 315 | 316 | emvl2Ret EmvL2Command::transmit(emvl2Apdu* apdu) { 317 | uint8_t commandData[APDUBUFFERLEN + 10] = { 0 }; 318 | uint16_t indexLen = 0; 319 | uint8_t readData[APDUBUFFERLEN] = { 0 }; 320 | uint32_t readDataLen = APDUBUFFERLEN; 321 | 322 | emvl2Ret rv = failure; 323 | 324 | apdu->SW1 = 0x00; 325 | apdu->SW2 = 0x00; 326 | apdu->rlen = 0x00; 327 | 328 | if ((apdu->CLA == 0xFF) && (apdu->INS == 0xFF)) 329 | { 330 | commandData[indexLen++] = apdu->Lc; 331 | memcpy(&commandData[indexLen], apdu->sdata, apdu->Lc); 332 | indexLen += apdu->Lc; 333 | } 334 | else 335 | { 336 | commandData[indexLen++] = apdu->CLA; 337 | commandData[indexLen++] = apdu->INS; 338 | commandData[indexLen++] = apdu->P1; 339 | commandData[indexLen++] = apdu->P2; 340 | 341 | if (apdu->Lc != 0xFF || apdu->forceData) 342 | { 343 | commandData[indexLen++] = apdu->Lc; 344 | memcpy(&commandData[indexLen], apdu->sdata, apdu->Lc); 345 | indexLen += apdu->Lc; 346 | } 347 | 348 | if (device->cardProtocol() == 1 && apdu->Le != 0xFF) 349 | { 350 | commandData[indexLen++] = apdu->Le; 351 | } 352 | 353 | if (device->cardProtocol() == 0 && apdu->Le != 0) 354 | { 355 | if (apdu->Lc == 0xFF) 356 | { 357 | commandData[indexLen++] = apdu->Le; 358 | } 359 | } 360 | } 361 | 362 | rv = device->cardSendReceive(commandData, indexLen, readData, &readDataLen); 363 | if (rv != success) 364 | { 365 | return cardCommError; 366 | } 367 | 368 | if (readDataLen > APDUBUFFERLEN) 369 | { 370 | return cardDataLenError; 371 | } 372 | 373 | apdu->rlen = (int)readDataLen; 374 | apdu->SW1 = readData[readDataLen - 2]; 375 | apdu->SW2 = readData[readDataLen - 1]; 376 | memcpy(apdu->rdata, readData, apdu->rlen); 377 | 378 | return success; 379 | } 380 | -------------------------------------------------------------------------------- /corelib/emvl2Command.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | 6 | using namespace std; 7 | 8 | class EmvL2Command { 9 | private: 10 | EmvL2Command(); 11 | public: 12 | static EmvL2Command& getInstance(); 13 | ~EmvL2Command(); 14 | emvl2Ret init(IDevice* device); 15 | 16 | emvl2Ret generateAC(uint8_t* data, uint8_t len, uint8_t cid); 17 | emvl2Ret getProcessingOptions(uint8_t* data, uint8_t len); 18 | emvl2Ret select(uint8_t* data, uint8_t len, uint8_t p2 = 0x00); 19 | emvl2Ret readRecord(uint8_t sfi, uint8_t idx); 20 | emvl2Ret internalAuthenticate(uint8_t* data, uint8_t len); 21 | emvl2Ret externalAuthenticate(); 22 | emvl2Ret getData(uint32_t tag); 23 | emvl2Ret verify(uint8_t type, uint8_t* data, uint8_t len); 24 | emvl2Ret getChallenge(); 25 | 26 | private: 27 | emvl2Ret performCommand1(emvl2Apdu* apdu); 28 | emvl2Ret performCommand2(emvl2Apdu* apdu); 29 | emvl2Ret performCommand3(emvl2Apdu* apdu); 30 | emvl2Ret performCommand4(emvl2Apdu* apdu); 31 | emvl2Ret transmit(emvl2Apdu* apdu); 32 | 33 | private: 34 | static EmvL2Command* instance; 35 | IDevice* device; 36 | EmvL2Repo& repo; 37 | }; 38 | -------------------------------------------------------------------------------- /corelib/emvl2Definitions.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thearistotlemethod/EMVKernel/ad1c8d7eb038aed8771578169a8d1cafbf4a7bfd/corelib/emvl2Definitions.cpp -------------------------------------------------------------------------------- /corelib/emvl2Defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "emvl2TagDef.h" 5 | 6 | #define PUBKEYMODULUSLEN 256 7 | #define APDUBUFFERLEN (256 + 2) 8 | #define DOLBUFFERLEN 512 9 | 10 | #define AAC 0x00 11 | #define TC 0x40 12 | #define ARQC 0x80 13 | #define AAR 0xC0 14 | #define ADVICE 0x08 15 | 16 | typedef enum 17 | { 18 | success = 0, 19 | failure, 20 | fallback, 21 | cardCommError, 22 | cardDataLenError, 23 | noMatchingApp, 24 | pseNotSupportedByCard, 25 | emvDataFormatError, 26 | emvMissingMandatoryDataError, 27 | emvLenError, 28 | noMemory, 29 | dublicateData, 30 | cardRejected, 31 | cardBlocked, 32 | aflLenError, 33 | sfiLenError, 34 | aflDataError, 35 | noTag, 36 | expDateFormatError, 37 | effDateFormatError, 38 | cvmAipNotSupported, 39 | cvmTag8EMissing, 40 | cvmTag8ERuleMissing, 41 | cvmTag8EFormatError, 42 | cvmCondCodeNotSupported, 43 | cvmIsNotSupported, 44 | pinFailed, 45 | pinCancel, 46 | pinTimeout, 47 | pinMalfunction, 48 | pinPrmError, 49 | cvmTypeUnknown, 50 | pintryCountError, 51 | emvCryptogramTypeError, 52 | genSecACWarning, 53 | emvServiceNotAllowed, 54 | emvSelectAppRetry, 55 | amountError, 56 | trnTypeError, 57 | aipNotFound, 58 | aflNotFound, 59 | aac, 60 | tc, 61 | arqc, 62 | aar 63 | } emvl2Ret; 64 | 65 | typedef struct 66 | { 67 | uint32_t tag; 68 | uint32_t len; 69 | std::vector val; 70 | }Tlv; 71 | 72 | typedef struct 73 | { 74 | uint8_t day; 75 | uint8_t month; 76 | uint16_t year; 77 | uint8_t second; 78 | uint8_t minute; 79 | uint8_t hour; 80 | } emvl2DateTime; 81 | 82 | typedef struct 83 | { 84 | uint8_t ucRid[5]; 85 | uint8_t ucPKExp[3]; 86 | uint8_t ucPKExpLen; 87 | uint8_t ucPKModulo[256]; 88 | uint8_t ucPKModuloLen; 89 | uint8_t ucPKIndex; 90 | } emvl2CAKey; 91 | 92 | typedef struct 93 | { 94 | uint8_t aidLen; 95 | uint8_t *aid; 96 | int len; 97 | uint8_t* data; 98 | } emvl2AIDPrms; 99 | 100 | typedef struct 101 | { 102 | Tlv tag9F06; 103 | Tlv tag84; 104 | Tlv tag4F; 105 | Tlv tag50; 106 | Tlv tag87; 107 | Tlv tag9F38; 108 | Tlv tag5F2D; 109 | Tlv tag5F56; 110 | Tlv tag9F11; 111 | Tlv tag9F12; 112 | Tlv tagBF0C; 113 | Tlv tag5F55; 114 | Tlv tag42; 115 | } emvl2AidInfo; 116 | 117 | typedef struct 118 | { 119 | uint8_t CLA; 120 | uint8_t INS; 121 | uint8_t P1; 122 | uint8_t P2; 123 | uint8_t Lc; 124 | uint8_t* sdata; 125 | uint8_t Le; 126 | uint8_t rdata[APDUBUFFERLEN]; 127 | uint16_t rlen; 128 | uint8_t SW1; 129 | uint8_t SW2; 130 | bool forceData; 131 | } emvl2Apdu; 132 | 133 | typedef enum 134 | { 135 | FNULL, 136 | FANS, 137 | FAN, 138 | FNUM, 139 | FCNM, 140 | } emvl2TagDataFormat; 141 | 142 | typedef enum 143 | { 144 | ANULL, 145 | ASDA, 146 | ADDA, 147 | ACDA 148 | } emvl2DataAuth; 149 | 150 | typedef enum 151 | { 152 | PKCAMOD = 0, 153 | PKICCMOD, 154 | PKISSMOD, 155 | PKPINMOD 156 | } emvl2PkMode; 157 | 158 | -------------------------------------------------------------------------------- /corelib/emvl2GPO.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2GPO.h" 2 | 3 | #define SWAPUINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24)) 4 | 5 | EmvL2GPO::EmvL2GPO(IDevice& device) : device(device), repo(EmvL2Repo::getInstance()), 6 | command(EmvL2Command::getInstance()), util(EmvL2Util::getInstance()), secUtil(EmvL2SecUtil::getInstance()) { 7 | } 8 | 9 | EmvL2GPO::~EmvL2GPO() { 10 | 11 | } 12 | 13 | uint8_t EmvL2GPO::perform(uint8_t ttype, uint8_t atype, const char* amt, const char* oamt) { 14 | uint8_t pdolData[DOLBUFFERLEN]; 15 | uint8_t pdolDataLen = 0; 16 | int nRet; 17 | 18 | repo.staticAppData.clear(); 19 | repo.transactionHashData.clear(); 20 | 21 | if ((nRet = initizalizeTerminalTags(ttype, atype, amt, oamt)) != success) 22 | { 23 | return nRet; 24 | } 25 | 26 | if ((nRet = prepPDOL(pdolData, &pdolDataLen)) != success) 27 | { 28 | return nRet; 29 | } 30 | 31 | nRet = command.getProcessingOptions(pdolData, pdolDataLen); 32 | if (nRet != success) 33 | { 34 | return nRet; 35 | } 36 | 37 | if ((nRet = processGPOResponse()) != success) 38 | { 39 | return nRet; 40 | } 41 | 42 | repo.setTag(0x9F34, (uint8_t*)"\x3F\x00\x00", 3); 43 | return success; 44 | } 45 | 46 | int EmvL2GPO::initizalizeTerminalTags(uint8_t ttype, uint8_t atype, const char* amt, const char* oamt) 47 | { 48 | int nRet = 0; 49 | uint8_t bamt[15] = { 0 }; 50 | uint8_t boamt[15] = { 0 }; 51 | 52 | clearNonAppSelectionTags(); 53 | 54 | util.str2Bcd((uint8_t*)amt, (uint16_t)strlen(amt), bamt, 6); 55 | util.str2Bcd((uint8_t*)oamt, (uint16_t)strlen(oamt), boamt, 6); 56 | 57 | if ((nRet = setAmountTags(ttype, bamt, boamt)) != success) 58 | { 59 | return nRet; 60 | } 61 | 62 | fillDynamicTags(); 63 | 64 | repo.setTag(0x5F57, &atype, 1); 65 | repo.setTag(0x9C, &ttype, 1); 66 | repo.setTag(0x95, NULL, 5); 67 | repo.setTag(0x9B, NULL, 2); 68 | repo.setTag(0x8A, NULL, 2); 69 | 70 | return success; 71 | } 72 | 73 | void EmvL2GPO::clearNonAppSelectionTags() 74 | { 75 | repo.setTag(0x42); 76 | repo.setTag(0x57); 77 | repo.setTag(0x58); 78 | repo.setTag(0x5A); 79 | repo.setTag(0x6F); 80 | repo.setTag(0x71); 81 | repo.setTag(0x72); 82 | repo.setTag(0x73); 83 | repo.setTag(0x82); 84 | repo.setTag(0x88); 85 | repo.setTag(0x8C); 86 | repo.setTag(0x8D); 87 | repo.setTag(0x8E); 88 | repo.setTag(0x8F); 89 | repo.setTag(0x90); 90 | repo.setTag(0x91); 91 | repo.setTag(0x92); 92 | repo.setTag(0x93); 93 | repo.setTag(0x94); 94 | repo.setTag(0x97); 95 | repo.setTag(0x5F20); 96 | repo.setTag(0x5F24); 97 | repo.setTag(0x5F25); 98 | repo.setTag(0x5F28); 99 | repo.setTag(0x5F30); 100 | repo.setTag(0x5F34); 101 | repo.setTag(0x5F55); 102 | repo.setTag(0x5F56); 103 | repo.setTag(0x9F05); 104 | repo.setTag(0x9F07); 105 | repo.setTag(0x9F08); 106 | repo.setTag(0x9F0B); 107 | repo.setTag(0x9F0D); 108 | repo.setTag(0x9F0E); 109 | repo.setTag(0x9F0F); 110 | repo.setTag(0x9F10); 111 | repo.setTag(0x9F13); 112 | repo.setTag(0x9F14); 113 | repo.setTag(0x9F17); 114 | repo.setTag(0x9F19); 115 | repo.setTag(0x9F1F); 116 | repo.setTag(0x9F20); 117 | repo.setTag(0x9F23); 118 | repo.setTag(0x9F24); 119 | repo.setTag(0x9F25); 120 | repo.setTag(0x9F26); 121 | repo.setTag(0x9F27); 122 | repo.setTag(0x9F2D); 123 | repo.setTag(0x9F2E); 124 | repo.setTag(0x9F2F); 125 | repo.setTag(0x9F32); 126 | repo.setTag(0x9F36); 127 | repo.setTag(0x9F3B); 128 | repo.setTag(0x9F42); 129 | repo.setTag(0x9F43); 130 | repo.setTag(0x9F44); 131 | repo.setTag(0x9F45); 132 | repo.setTag(0x9F46); 133 | repo.setTag(0x9F47); 134 | repo.setTag(0x9F48); 135 | repo.setTag(0x9F49); 136 | repo.setTag(0x9F4A); 137 | repo.setTag(0x9F4B); 138 | repo.setTag(0x9F4C); 139 | repo.setTag(0x9F4D); 140 | } 141 | 142 | int EmvL2GPO::setAmountTags(uint8_t ttype, uint8_t* bamt, uint8_t* boamt) 143 | { 144 | uint32_t authAmt = 0, authAmtOther = 0; 145 | uint8_t bcdAmt[6], otherAmt[13]; 146 | char amt[15] = { 0 }, oamt[15] = { 0 }, trnAmt[13] = { 0 }; 147 | 148 | util.bcd2Str(bamt, 6, (uint8_t *)amt); 149 | util.bcd2Str(boamt, 6, (uint8_t*)oamt); 150 | 151 | if (ttype == TRNCASHBACK) 152 | { 153 | sprintf(trnAmt, "%012d", atoi(amt) + atoi(oamt)); 154 | } 155 | else 156 | { 157 | strcpy((char *)trnAmt, amt); 158 | } 159 | 160 | if (ttype != 0x09) 161 | { 162 | strcpy((char *)oamt, "0"); 163 | } 164 | 165 | if (util.str2Bcd((uint8_t*)trnAmt, (uint16_t)strlen((char *)trnAmt), bcdAmt, 6) == NULL) 166 | { 167 | return amountError; 168 | } 169 | 170 | if (trnAmt[0] == '0' && trnAmt[1] == '0') 171 | { 172 | if (strspn(trnAmt, "0123456789") != strlen(trnAmt)) { 173 | return amountError; 174 | } 175 | 176 | authAmt = SWAPUINT32(atoi(trnAmt)); 177 | } 178 | 179 | if (util.str2Bcd((uint8_t *)oamt, (uint16_t)strlen(oamt), otherAmt, 6) == NULL) 180 | { 181 | return amountError; 182 | } 183 | 184 | if (strlen(oamt) <= 10) 185 | { 186 | if (strspn(oamt, "0123456789") != strlen(oamt)) { 187 | return amountError; 188 | } 189 | 190 | authAmtOther = SWAPUINT32(atoi(oamt)); 191 | } 192 | 193 | repo.setTag(0x9F02, bcdAmt, 6); 194 | repo.setTag(0x9F03, otherAmt, 6); 195 | repo.setTag(0x81, (uint8_t*) & authAmt, 4); 196 | repo.setTag(0x9F04, (uint8_t*) & authAmtOther, 4); 197 | return success; 198 | 199 | } 200 | 201 | void EmvL2GPO::fillDynamicTags() 202 | { 203 | uint8_t UN[4]; 204 | int ret; 205 | emvl2DateTime dateTime; 206 | 207 | ret = device.getDateTime(&dateTime); 208 | if (success == ret) 209 | { 210 | uint8_t tmpBuff[16]; 211 | tmpBuff[0] = (uint8_t)util.byte2Bcd(dateTime.hour); 212 | tmpBuff[1] = (uint8_t)util.byte2Bcd(dateTime.minute); 213 | tmpBuff[2] = (uint8_t)util.byte2Bcd(dateTime.second); 214 | tmpBuff[3] = (uint8_t)util.byte2Bcd(util.adjustYear(dateTime.year)); 215 | tmpBuff[4] = (uint8_t)util.byte2Bcd(dateTime.month); 216 | tmpBuff[5] = (uint8_t)util.byte2Bcd(dateTime.day); 217 | 218 | repo.setTag(0x9F21, tmpBuff, 3); 219 | repo.setTag(0x9A, &tmpBuff[3], 3); 220 | 221 | ret = device.genRand(UN, sizeof(UN)); 222 | if (success == ret) 223 | { 224 | repo.setTag(0x9F37, UN, sizeof(UN)); 225 | } 226 | } 227 | } 228 | 229 | int EmvL2GPO::prepPDOL(uint8_t* pdolData, uint8_t* pdolLen) 230 | { 231 | int iPdolLen = 0; 232 | uint8_t counterPDOL = 2; 233 | uint8_t tempPDOLData[DOLBUFFERLEN]; 234 | int len = 0; 235 | uint8_t* pucPDOL = NULL; 236 | 237 | Tlv* tag9F38 = repo.getTag(0x9F38); 238 | if (tag9F38) { 239 | len = tag9F38->len; 240 | pucPDOL = tag9F38->val.data(); 241 | } 242 | 243 | iPdolLen = len; 244 | pdolData[0] = 0x83; 245 | 246 | if (iPdolLen == 0) 247 | { 248 | pdolData[1] = 0x00; 249 | } 250 | else 251 | { 252 | util.collectDolData(pucPDOL, iPdolLen, tempPDOLData, pdolLen); 253 | if ((*pdolLen & 0x80) == 0x80) 254 | { 255 | pdolData[1] = 0x81; 256 | pdolData[2] = *pdolLen; 257 | memcpy(&pdolData[3], tempPDOLData, *pdolLen); 258 | (*pdolLen)++; 259 | counterPDOL = 3; 260 | } 261 | else 262 | { 263 | pdolData[1] = *pdolLen; 264 | memcpy(&pdolData[2], tempPDOLData, *pdolLen); 265 | } 266 | } 267 | 268 | *pdolLen += 2; 269 | 270 | repo.transactionHashData.insert(repo.transactionHashData.end(), &pdolData[counterPDOL], &pdolData[counterPDOL] + (*pdolLen - counterPDOL)); 271 | return success; 272 | } 273 | 274 | int EmvL2GPO::processGPOResponse() 275 | { 276 | int len, iMsgIndex = 0, nRet = 0, i; 277 | uint8_t lenLen, sw1, sw2; 278 | 279 | sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 280 | sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 281 | 282 | if ((sw1 == 0x69) && (sw2 == 0x85)) 283 | { 284 | return emvSelectAppRetry; 285 | } 286 | 287 | if ((sw1 != 0x90) || (sw2 != 0x00)) 288 | { 289 | return cardRejected; 290 | } 291 | 292 | while ((repo.apdu.rdata[iMsgIndex] == 0) || (repo.apdu.rdata[iMsgIndex] == 0xFF)) 293 | { 294 | iMsgIndex++; 295 | } 296 | 297 | if (repo.apdu.rdata[iMsgIndex] == 0x80) 298 | { 299 | iMsgIndex++; 300 | 301 | if ((repo.apdu.rdata[iMsgIndex] & 0x80) == 0x80) 302 | { 303 | len = repo.parseLen(&repo.apdu.rdata[iMsgIndex], &lenLen); 304 | iMsgIndex += lenLen; 305 | } 306 | else 307 | { 308 | lenLen = 1; 309 | len = repo.apdu.rdata[iMsgIndex++]; 310 | } 311 | 312 | if (len != repo.apdu.rlen - 3 - lenLen) 313 | { 314 | return emvLenError; 315 | } 316 | 317 | repo.setTag(0x82, repo.apdu.rdata + iMsgIndex, 2); 318 | iMsgIndex += 2; 319 | 320 | if (((len - 2) % 4) != 0) 321 | { 322 | return emvDataFormatError; 323 | } 324 | 325 | for (i = iMsgIndex; i < (len - 2); i = i + 4) 326 | { 327 | if (repo.apdu.rdata[i] == 0) 328 | { 329 | return emvDataFormatError; 330 | } 331 | } 332 | 333 | repo.setTag(0x94, repo.apdu.rdata + iMsgIndex, len - 2); 334 | iMsgIndex += len - 2; 335 | } 336 | else if (repo.apdu.rdata[iMsgIndex] == 0x77) 337 | { 338 | iMsgIndex++; 339 | 340 | if ((repo.apdu.rdata[iMsgIndex] & 0x80) == 0x80) 341 | { 342 | lenLen = repo.apdu.rdata[iMsgIndex] & 0x7F; 343 | len = util.bin2Int(&repo.apdu.rdata[iMsgIndex + 1], lenLen); 344 | iMsgIndex += lenLen + 1; 345 | } 346 | else 347 | { 348 | len = repo.apdu.rdata[iMsgIndex++]; 349 | } 350 | 351 | nRet = repo.parseTags(&repo.apdu.rdata[iMsgIndex], len); 352 | if (nRet != success) 353 | { 354 | return nRet; 355 | } 356 | 357 | if (!repo.isTagExist(0x82)) 358 | { 359 | return aipNotFound; 360 | } 361 | 362 | if (!repo.isTagExist(0x94)) 363 | { 364 | return aflNotFound; 365 | } 366 | } 367 | else 368 | { 369 | return emvDataFormatError; 370 | } 371 | 372 | return success; 373 | } -------------------------------------------------------------------------------- /corelib/emvl2GPO.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Command.h" 6 | #include "emvl2Util.h" 7 | #include "emvl2SecUtil.h" 8 | 9 | using namespace std; 10 | 11 | class EmvL2GPO { 12 | public: 13 | EmvL2GPO(IDevice& device); 14 | ~EmvL2GPO(); 15 | uint8_t perform(uint8_t ttype, uint8_t atype, const char* amt, const char* oamt); 16 | 17 | private: 18 | int initizalizeTerminalTags(uint8_t ttype, uint8_t atype, const char* amt, const char* oamt); 19 | void clearNonAppSelectionTags(); 20 | int setAmountTags(uint8_t ttype, uint8_t* bamt, uint8_t* boamt); 21 | void fillDynamicTags(); 22 | int prepPDOL(uint8_t* data, uint8_t* len); 23 | int processGPOResponse(); 24 | 25 | private: 26 | IDevice& device; 27 | EmvL2Repo& repo; 28 | EmvL2Command& command; 29 | EmvL2Util& util; 30 | EmvL2SecUtil& secUtil; 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /corelib/emvl2GenAC1.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2GenAC1.h" 2 | #include "emvl2Command.h" 3 | 4 | EmvL2GenAC1::EmvL2GenAC1(IDevice& device) : device(device), repo(EmvL2Repo::getInstance()), 5 | command(EmvL2Command::getInstance()), util(EmvL2Util::getInstance()), secUtil(EmvL2SecUtil::getInstance()) { 6 | } 7 | 8 | EmvL2GenAC1::~EmvL2GenAC1() { 9 | 10 | } 11 | 12 | uint8_t EmvL2GenAC1::perform(uint8_t termDecision, uint8_t* cardDecision) { 13 | int rv; 14 | uint8_t cdol[256], cdolData[DOLBUFFERLEN], cdolLen; 15 | 16 | repo.adviceReversal = 0; 17 | 18 | Tlv* tag8C = repo.getTag(0x8C); 19 | if (!tag8C) 20 | return failure; 21 | 22 | int len = tag8C->len; 23 | memcpy(cdol, tag8C->val.data(), len); 24 | 25 | util.collectDolData(cdol, len, cdolData, &cdolLen); 26 | util.collectDolData(cdol, len, cdolData, &cdolLen); 27 | 28 | repo.transactionHashData.insert(repo.transactionHashData.end(), cdolData, cdolData + cdolLen); 29 | 30 | if (((termDecision == TC) || (termDecision == ARQC)) && (repo.typeOfAuth == ACDA) && ((repo.getTag(0x95)->val.data()[0] & CDAFAILED) != CDAFAILED)) 31 | { 32 | if ((rv = genAC1WithCDAProccessing(&termDecision, cdolData, cdolLen)) != success) 33 | { 34 | if (rv == genSecACWarning) 35 | { 36 | *cardDecision = AAC; 37 | return success; 38 | } 39 | else 40 | { 41 | return rv; 42 | } 43 | } 44 | } 45 | else 46 | { 47 | if ((rv = genAC1WithoutCDAProccessing(&termDecision, cdolData, cdolLen)) != success) 48 | { 49 | return rv; 50 | } 51 | } 52 | 53 | return genAC1DecisionProccessing(termDecision, cardDecision); 54 | } 55 | 56 | int EmvL2GenAC1::genAC1DecisionProccessing(uint8_t termDecision, uint8_t* cardDecision) 57 | { 58 | Tlv* tag9F27 = repo.getTag(0x9F27); 59 | 60 | *cardDecision = tag9F27->val.data()[0]; 61 | if (((*cardDecision) & ADVICE) == ADVICE) 62 | { 63 | repo.adviceReversal = repo.adviceReversal | 0x01; 64 | } 65 | 66 | if ((tag9F27->val.data()[0] & 0x03) == SERVICE_NOT_ALLOWED) 67 | { 68 | return emvServiceNotAllowed; 69 | } 70 | 71 | if (termDecision == AAC) 72 | { 73 | if (((*cardDecision & 0xC0) == ARQC) || ((*cardDecision & 0xC0) == TC)) 74 | { 75 | return emvCryptogramTypeError; 76 | } 77 | } 78 | 79 | if (termDecision == ARQC) 80 | { 81 | if ((*cardDecision & 0xC0) == TC) 82 | { 83 | return emvCryptogramTypeError; 84 | } 85 | } 86 | 87 | if ((*cardDecision & TC) == TC) 88 | { 89 | repo.setTag(0x8A, (uint8_t*)"Y1", 2); 90 | } 91 | else 92 | { 93 | repo.setTag(0x8A, (uint8_t*)"\x00\x00", 2); 94 | } 95 | 96 | return success; 97 | } 98 | 99 | int EmvL2GenAC1::genAC1WithoutCDAProccessing(uint8_t* termDecision, uint8_t* cdolData, uint8_t cdolLen) 100 | { 101 | uint8_t sw1, sw2; 102 | uint8_t lenLen; 103 | int rv; 104 | int dataElIndex, dataElIndexPrev, msgIndex = 1; 105 | 106 | rv = command.generateAC(cdolData, cdolLen, *termDecision); 107 | if (rv != success) 108 | { 109 | return rv; 110 | } 111 | 112 | sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 113 | sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 114 | 115 | if ((sw1 != 0x90) || (sw2 != 0x00)) 116 | { 117 | return cardRejected; 118 | } 119 | 120 | if ((repo.apdu.rdata[0] != 0x80) && (repo.apdu.rdata[0] != 0x77)) 121 | { 122 | return emvDataFormatError; 123 | } 124 | 125 | Tlv* tag9F27 = repo.getTag(0x9F27); 126 | if (!tag9F27) { 127 | repo.setTag(0x9F27, termDecision, 1); 128 | tag9F27 = repo.getTag(0x9F27); 129 | } 130 | 131 | if (repo.apdu.rdata[0] == 0x80) 132 | { 133 | if ((rv = secUtil.genACNOCDATemplate80Processing()) != success) 134 | { 135 | return rv; 136 | } 137 | 138 | if ((tag9F27->val.data()[0] & AAR) == AAR) 139 | { 140 | return emvCryptogramTypeError; 141 | } 142 | } 143 | else if (repo.apdu.rdata[0] == 0x77) 144 | { 145 | repo.parseLen(&repo.apdu.rdata[msgIndex], &lenLen); 146 | dataElIndex = 1 + lenLen; 147 | 148 | while (dataElIndex < repo.apdu.rlen - 2) 149 | { 150 | dataElIndexPrev = dataElIndex; 151 | repo.nextTag(repo.apdu.rdata, &dataElIndex); 152 | 153 | if ((repo.apdu.rdata[dataElIndexPrev] != 0x9F) || (repo.apdu.rdata[dataElIndexPrev + 1] != 0x4B)) 154 | { 155 | repo.transactionHashData.insert(repo.transactionHashData.end(), &repo.apdu.rdata[dataElIndexPrev], &repo.apdu.rdata[dataElIndexPrev] + (dataElIndex - dataElIndexPrev)); 156 | } 157 | } 158 | 159 | if ((rv = secUtil.genACNOCDATemplate77Processing()) != success) 160 | { 161 | return rv; 162 | } 163 | 164 | if ((tag9F27->val.data()[0] & AAR) == AAR) 165 | { 166 | return emvCryptogramTypeError; 167 | } 168 | } 169 | 170 | return success; 171 | } 172 | 173 | int EmvL2GenAC1::genAC1WithCDAProccessing(uint8_t* termDecision, uint8_t* cdolData, uint8_t cdolLen) 174 | { 175 | uint8_t sw1, sw2; 176 | int rv; 177 | uint8_t cid; 178 | int genACTCHashIndex = 0; 179 | 180 | repo.verifyDDAACFail = failure; 181 | *termDecision = *termDecision + 0x10; 182 | 183 | rv = command.generateAC(cdolData, cdolLen, *termDecision); 184 | if (rv != success) 185 | { 186 | return rv; 187 | } 188 | 189 | sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 190 | sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 191 | 192 | if ((sw1 != 0x90) || (sw2 != 0x00)) 193 | { 194 | return cardRejected; 195 | } 196 | 197 | if ((repo.apdu.rdata[0] != 0x80) && (repo.apdu.rdata[0] != 0x77)) 198 | { 199 | return emvDataFormatError; 200 | } 201 | 202 | if (repo.apdu.rdata[0] == 0x80) 203 | { 204 | if ((rv = secUtil.genACCDATemplate80Processing()) != success) 205 | { 206 | return rv; 207 | } 208 | 209 | Tlv* tag9F27 = repo.getTag(0x9F27); 210 | if ((tag9F27->val.data()[0] & AAR) == AAR) 211 | { 212 | return emvCryptogramTypeError; 213 | } 214 | 215 | if (((tag9F27->val.data()[0] & 0xC0) != AAC) && ((tag9F27->val.data()[0] & AAR) != AAR)) 216 | { 217 | if ((tag9F27->val.data()[0] & 0xC0) == ARQC) 218 | { 219 | repo.performImeediateSecondGenAc = success; 220 | } 221 | else 222 | { 223 | repo.performImeediateSecondGenAc = failure; 224 | } 225 | 226 | return genSecACWarning; 227 | } 228 | } 229 | else if (repo.apdu.rdata[0] == 0x77) 230 | { 231 | genACTCHashIndex = repo.transactionHashData.size(); 232 | if ((rv = secUtil.genACCDATemplate77Processing(true)) != success) 233 | { 234 | return rv; 235 | } 236 | 237 | Tlv* tag9F27 = repo.getTag(0x9F27); 238 | if ((tag9F27->val.data()[0] & AAR) == AAR) 239 | { 240 | return emvCryptogramTypeError; 241 | } 242 | 243 | rv = secUtil.verifyDynamicSignAC(PKICCMOD, repo.cdaIccPkModLen, &cid); 244 | repo.transactionHashData.resize(genACTCHashIndex); 245 | 246 | if (rv == failure) 247 | { 248 | repo.setTagFlag(0x95, CDAFAILED); 249 | 250 | if (((cid & 0xC0) != TC) && ((cid & AAR) != AAR) && ((cid & AAR) != 0x00)) 251 | { 252 | repo.performImeediateSecondGenAc = success; 253 | } 254 | 255 | repo.verifyDDAACFail = success; 256 | return genSecACWarning; 257 | } 258 | } 259 | 260 | return success; 261 | } -------------------------------------------------------------------------------- /corelib/emvl2GenAC1.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Command.h" 6 | #include "emvl2Util.h" 7 | #include "emvl2SecUtil.h" 8 | 9 | using namespace std; 10 | 11 | class EmvL2GenAC1 { 12 | public: 13 | EmvL2GenAC1(IDevice& device); 14 | ~EmvL2GenAC1(); 15 | uint8_t perform(uint8_t ucTermDecision, uint8_t* ucCardDecision); 16 | 17 | private: 18 | int genAC1WithCDAProccessing(uint8_t* termDecision, uint8_t* cdolData, uint8_t cdolLen); 19 | int genAC1WithoutCDAProccessing(uint8_t* termDecision, uint8_t* cdolData, uint8_t cdolLen); 20 | int genAC1DecisionProccessing(uint8_t termDecision, uint8_t* cardDecision); 21 | 22 | private: 23 | IDevice& device; 24 | EmvL2Repo& repo; 25 | EmvL2Command& command; 26 | EmvL2Util& util; 27 | EmvL2SecUtil& secUtil; 28 | }; 29 | -------------------------------------------------------------------------------- /corelib/emvl2GenAC2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Command.h" 6 | #include "emvl2Util.h" 7 | #include "emvl2SecUtil.h" 8 | 9 | using namespace std; 10 | 11 | class EmvL2GenAC2 { 12 | public: 13 | EmvL2GenAC2(IDevice& device); 14 | ~EmvL2GenAC2(); 15 | uint8_t perform(bool isHostReject, uint8_t* decision, uint8_t* adviceReversal); 16 | 17 | private: 18 | int completion(bool isHostReject, uint8_t* cardDecision, uint8_t* adviceReversal); 19 | void termActionAnalysisDefault(uint8_t* termDecision); 20 | void issuerAuthentication(); 21 | int issuerScriptProcessing71(); 22 | int issuerScriptProcessing72(); 23 | int genAC2WithCDAProccessing(uint8_t* termDecision, uint8_t* cardDecision, uint8_t* cdolData, uint8_t cdolLen); 24 | int genAC2WithoutCDAProccessing(uint8_t* termDecision, uint8_t* cardDecision, uint8_t* cdolData, uint8_t cdolLen); 25 | void genAC2DecisionProccessing(bool isHostReject, uint8_t* termDecision, uint8_t* cardDecision, uint8_t* adviceReversal); 26 | 27 | private: 28 | IDevice& device; 29 | EmvL2Repo& repo; 30 | EmvL2Command& command; 31 | EmvL2Util& util; 32 | EmvL2SecUtil& secUtil; 33 | 34 | uint8_t g_ucCmdSeqNo; 35 | int g_iTotalScriptMsgLen; 36 | uint8_t g_ucScriptIndex; 37 | }; -------------------------------------------------------------------------------- /corelib/emvl2Impl.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2Impl.h" 2 | #include "emvl2SecUtil.h" 3 | 4 | EmvL2Impl* EmvL2Impl::instance = NULL; 5 | 6 | EmvL2Impl& EmvL2Impl::getInstance() { 7 | if (!instance) { 8 | instance = new EmvL2Impl(); 9 | } 10 | return *instance; 11 | } 12 | 13 | EmvL2Impl::EmvL2Impl() { 14 | device = NULL; 15 | } 16 | 17 | EmvL2Impl::~EmvL2Impl() { 18 | 19 | } 20 | 21 | uint8_t EmvL2Impl::init(IDevice* device) { 22 | if (device == NULL) { 23 | return failure; 24 | } 25 | 26 | this->device = device; 27 | EmvL2Repo::getInstance().init(device); 28 | EmvL2Command::getInstance().init(device); 29 | EmvL2Util::getInstance().init(device); 30 | EmvL2SecUtil::getInstance().init(device); 31 | 32 | EmvL2Repo::getInstance().clearCaKeys(); 33 | EmvL2Repo::getInstance().clearAidPrms(); 34 | 35 | this->appSelectionIns = new EmvL2AppSelection(*device); 36 | this->gpoIns = new EmvL2GPO(*device); 37 | this->readAppDataIns = new EmvL2ReadAppData(*device); 38 | this->offlineDataAuthIns = new EmvL2OfflineDataAuth(*device); 39 | this->processRestrictIns = new EmvL2ProcessRestrict(*device); 40 | this->processCVMIns = new EmvL2ProcessCVM(*device); 41 | this->termActionAnalysisIns = new EmvL2TermActionAnalysis(*device); 42 | this->terminalRiskMngIns = new EmvL2TerminalRiskMng(*device); 43 | this->genAC1Ins = new EmvL2GenAC1(*device); 44 | this->genAC2Ins = new EmvL2GenAC2(*device); 45 | return success; 46 | } 47 | 48 | IDevice& EmvL2Impl::getDevice() { 49 | return *this->device; 50 | } 51 | 52 | void EmvL2Impl::release() { 53 | 54 | } 55 | 56 | uint8_t EmvL2Impl::initTransaction(uint8_t* FallbackOccured) { 57 | return success; 58 | } 59 | 60 | uint8_t EmvL2Impl::applicationSelection() { 61 | return this->appSelectionIns->perform(); 62 | } 63 | 64 | uint8_t EmvL2Impl::selectFromCandList() { 65 | return success; 66 | } 67 | 68 | uint8_t EmvL2Impl::selectMatchingApp(uint8_t* Language, int SelectedAppIndex) { 69 | return success; 70 | } 71 | 72 | uint8_t EmvL2Impl::gpo(uint8_t TransactionType, uint8_t AccountType, const char* Amount, const char* OtherAmount) { 73 | return this->gpoIns->perform(TransactionType, AccountType, Amount, OtherAmount); 74 | } 75 | 76 | uint8_t EmvL2Impl::readAppData() { 77 | return this->readAppDataIns->perform(); 78 | } 79 | 80 | uint8_t EmvL2Impl::offlineDataAuth() { 81 | return this->offlineDataAuthIns->perform(); 82 | } 83 | 84 | uint8_t EmvL2Impl::processRestrict() { 85 | return this->processRestrictIns->perform(); 86 | } 87 | 88 | uint8_t EmvL2Impl::processCVM() { 89 | return this->processCVMIns->perform(); 90 | } 91 | 92 | uint8_t EmvL2Impl::terminalRiskMng() { 93 | return this->terminalRiskMngIns->perform(); 94 | } 95 | 96 | uint8_t EmvL2Impl::termActionAnalysis(uint8_t* TerminalDecision) { 97 | return this->termActionAnalysisIns->perform(TerminalDecision); 98 | } 99 | 100 | uint8_t EmvL2Impl::genAC1(uint8_t TerminalDecision, uint8_t* CardDecision) { 101 | return this->genAC1Ins->perform(TerminalDecision, CardDecision); 102 | } 103 | 104 | uint8_t EmvL2Impl::genAC2(uint8_t isOnlineError, uint8_t* Decision, uint8_t* AdviceReversal) { 105 | return this->genAC2Ins->perform(isOnlineError, Decision, AdviceReversal); 106 | } 107 | 108 | uint8_t EmvL2Impl::addCAKey(uint8_t* rid, uint8_t keyId, uint8_t modulesLen, uint8_t* modules, uint8_t* exponent, uint8_t exponentLen) { 109 | emvl2CAKey key; 110 | memcpy(key.ucRid, rid, 5); 111 | key.ucPKIndex = keyId; 112 | key.ucPKModuloLen = modulesLen; 113 | memcpy(key.ucPKModulo, modules, modulesLen); 114 | memcpy(key.ucPKExp, exponent, exponentLen); 115 | key.ucPKExpLen = exponentLen; 116 | 117 | return EmvL2Repo::getInstance().addCaKey(key); 118 | } 119 | 120 | uint8_t EmvL2Impl::addAidPrms(emvl2AIDPrms prms) { 121 | return EmvL2Repo::getInstance().addAidPrms(prms); 122 | } 123 | 124 | uint8_t EmvL2Impl::forceOnline(void) { 125 | return success; 126 | } 127 | 128 | uint8_t EmvL2Impl::getEMVDataEl(uint32_t Tag, uint8_t* TagValue, uint16_t* TagValueLength, uint8_t* Format) { 129 | return success; 130 | } 131 | 132 | uint8_t EmvL2Impl::setEMVDataEl(uint8_t* tlvBuffer, uint8_t tlvBufferLen) { 133 | return success; 134 | } 135 | 136 | uint8_t EmvL2Impl::resetTransLogAmount(void) { 137 | return success; 138 | } 139 | 140 | void EmvL2Impl::version(char* vKernel) { 141 | if (vKernel != NULL) { 142 | strcpy(vKernel, LIBVERSION); 143 | } 144 | } 145 | 146 | uint8_t EmvL2Impl::setTranSeqCounter(uint32_t tscValue) { 147 | return success; 148 | } 149 | 150 | void EmvL2Impl::clearLogFiles() { 151 | 152 | } 153 | 154 | uint8_t EmvL2Impl::getChipFields(uint8_t* buffer, int length) { 155 | return success; 156 | } 157 | 158 | uint8_t EmvL2Impl::applyHostSystemResponse(uint8_t* buffer, int length) { 159 | return success; 160 | } 161 | 162 | void EmvL2Impl::setAccountType(uint8_t AccountType) { 163 | 164 | } 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /corelib/emvl2Impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2AppSelection.h" 6 | #include "emvl2ReadAppData.h" 7 | #include "emvl2OfflineDataAuth.h" 8 | #include "emvl2ProcessRestrict.h" 9 | #include "emvl2ProcessCVM.h" 10 | #include "emvl2TerminalRiskMng.h" 11 | #include "emvl2TermActionAnalysis.h" 12 | #include "emvl2GenAC1.h" 13 | #include "emvl2GenAC2.h" 14 | #include "emvl2Command.h" 15 | #include "emvl2GPO.h" 16 | 17 | #define LIBVERSION "1.0.0" 18 | 19 | class EmvL2Impl { 20 | private: 21 | EmvL2Impl(); 22 | 23 | public: 24 | static EmvL2Impl& getInstance(); 25 | ~EmvL2Impl(); 26 | 27 | uint8_t init(IDevice* device); 28 | IDevice& getDevice(); 29 | void release(); 30 | uint8_t initTransaction(uint8_t* FallbackOccured); 31 | uint8_t applicationSelection(); 32 | uint8_t selectFromCandList(); 33 | uint8_t selectMatchingApp(uint8_t* Language, int SelectedAppIndex); 34 | uint8_t gpo(uint8_t TransactionType, uint8_t AccountType, const char* Amount, const char* OtherAmount); 35 | uint8_t readAppData(); 36 | uint8_t offlineDataAuth(); 37 | uint8_t processRestrict(); 38 | uint8_t processCVM(); 39 | uint8_t terminalRiskMng(); 40 | uint8_t termActionAnalysis(uint8_t* TerminalDecision); 41 | uint8_t genAC1(uint8_t TerminalDecision, uint8_t* CardDecision); 42 | uint8_t genAC2(uint8_t isOnlineError, uint8_t* Decision, uint8_t* AdviceReversal); 43 | uint8_t addCAKey(uint8_t* rid, uint8_t keyId, uint8_t modulesLen, uint8_t* modules, uint8_t* exponent, uint8_t exponentLen); 44 | uint8_t addAidPrms(emvl2AIDPrms prms); 45 | uint8_t forceOnline(void); 46 | uint8_t getEMVDataEl(uint32_t Tag, uint8_t* TagValue, uint16_t* TagValueLength, uint8_t* Format); 47 | uint8_t setEMVDataEl(uint8_t* tlvBuffer, uint8_t tlvBufferLen); 48 | uint8_t resetTransLogAmount(void); 49 | void version(char* vKernel); 50 | uint8_t setTranSeqCounter(uint32_t tscValue); 51 | void clearLogFiles(); 52 | uint8_t getChipFields(uint8_t* buffer, int length); 53 | uint8_t applyHostSystemResponse(uint8_t* buffer, int length); 54 | void setAccountType(uint8_t AccountType); 55 | 56 | private: 57 | static EmvL2Impl* instance; 58 | IDevice* device = NULL; 59 | EmvL2AppSelection* appSelectionIns = NULL; 60 | EmvL2GPO* gpoIns = NULL; 61 | EmvL2ReadAppData* readAppDataIns = NULL; 62 | EmvL2OfflineDataAuth* offlineDataAuthIns = NULL; 63 | EmvL2ProcessRestrict* processRestrictIns = NULL; 64 | EmvL2ProcessCVM* processCVMIns = NULL; 65 | EmvL2TerminalRiskMng* terminalRiskMngIns = NULL; 66 | EmvL2TermActionAnalysis* termActionAnalysisIns = NULL; 67 | EmvL2GenAC1* genAC1Ins = NULL; 68 | EmvL2GenAC2* genAC2Ins = NULL; 69 | }; -------------------------------------------------------------------------------- /corelib/emvl2OfflineDataAuth.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2OfflineDataAuth.h" 2 | #include "emvl2Command.h" 3 | 4 | EmvL2OfflineDataAuth::EmvL2OfflineDataAuth(IDevice& device) : device(device), repo(EmvL2Repo::getInstance()), 5 | command(EmvL2Command::getInstance()), util(EmvL2Util::getInstance()), secUtil(EmvL2SecUtil::getInstance()) { 6 | } 7 | 8 | EmvL2OfflineDataAuth::~EmvL2OfflineDataAuth() { 9 | 10 | } 11 | 12 | uint8_t EmvL2OfflineDataAuth::perform() { 13 | int rv; 14 | 15 | repo.typeOfAuth = ANULL; 16 | 17 | Tlv* tag8F = repo.getTag(0x8F); 18 | if (tag8F) { 19 | repo.setTag(0x9F22, tag8F->val.data(), tag8F->len); 20 | } 21 | 22 | rv = secUtil.determineDataAuthType(&repo.typeOfAuth); 23 | if (rv != success) 24 | { 25 | return rv; 26 | } 27 | 28 | if (!repo.isTagExist(0x8F)) 29 | { 30 | if ((repo.isTagFlag(0x82, SDASupported)) || (repo.isTagFlag(0x82, DDASupported)) || (repo.isTagFlag(0x82, CDASupported))) 31 | { 32 | repo.setTagFlag(0x95, ICCDATAMISSING); 33 | } 34 | 35 | if (repo.isTagFlag(0x82, SDASupported)) 36 | { 37 | repo.setTagFlag(0x95, SDAFAILED); 38 | repo.setTagFlag(0x9B, OFFAUTHPERFORMED); 39 | } 40 | 41 | if (repo.isTagFlag(0x82, DDASupported)) 42 | { 43 | repo.setTagFlag(0x95, DDAFAILED); 44 | repo.setTagFlag(0x9B, OFFAUTHPERFORMED); 45 | } 46 | 47 | if (repo.isTagFlag(0x82, CDASupported)) 48 | { 49 | repo.setTagFlag(0x95, CDAFAILED); 50 | repo.setTagFlag(0x9B, OFFAUTHPERFORMED); 51 | } 52 | 53 | return success; 54 | } 55 | 56 | if (repo.typeOfAuth == ASDA) 57 | { 58 | repo.setTagFlag(0x95, SDASELECTED); 59 | rv = performSDA(); 60 | if (rv != success) 61 | { 62 | return rv; 63 | } 64 | } 65 | else if (repo.typeOfAuth == ADDA) 66 | { 67 | rv = performDDA(); 68 | if (rv != success) 69 | { 70 | return rv; 71 | } 72 | } 73 | else if (repo.typeOfAuth == ACDA) 74 | { 75 | rv = performCDA(); 76 | if (rv != success) 77 | { 78 | return rv; 79 | } 80 | } 81 | 82 | return success; 83 | } 84 | 85 | int EmvL2OfflineDataAuth::performCDA() 86 | { 87 | int len, iExpLen; 88 | uint8_t issModLen, pkModLen; 89 | uint8_t exponent[3]; 90 | uint8_t ddolData[DOLBUFFERLEN]; 91 | 92 | memset(repo.caPkModulus, 0, sizeof(repo.caPkModulus)); 93 | memset(repo.issPkModulus, 0, sizeof(repo.issPkModulus)); 94 | memset(ddolData, 0, sizeof(ddolData)); 95 | 96 | if (!repo.isTagExist(0x95) || !repo.isTagExist(0x9B)) 97 | { 98 | return emvMissingMandatoryDataError; 99 | } 100 | 101 | repo.setTagFlag(0x9B, OFFAUTHPERFORMED); 102 | if (!repo.isTagExist(0x8F)) 103 | { 104 | repo.setTagFlag(0x95, ICCDATAMISSING); 105 | repo.setTagFlag(0x95, CDAFAILED); 106 | return success; 107 | } 108 | 109 | if (failure == repo.searchCAKeys()) 110 | { 111 | repo.setTagFlag(0x95, CDAFAILED); 112 | return success; 113 | } 114 | 115 | pkModLen = repo.activeCAKey->ucPKModuloLen; 116 | memcpy(repo.caPkModulus, &repo.activeCAKey->ucPKModulo, pkModLen); 117 | iExpLen = repo.activeCAKey->ucPKExpLen; 118 | memcpy(exponent, repo.activeCAKey->ucPKExp, iExpLen); 119 | 120 | if (!repo.isTagExist(0x90)) 121 | { 122 | repo.setTagFlag(0x95, ICCDATAMISSING); 123 | repo.setTagFlag(0x95, CDAFAILED); 124 | return success; 125 | } 126 | 127 | if (!repo.isTagExist(0x9F32)) 128 | { 129 | repo.setTagFlag(0x95, ICCDATAMISSING); 130 | repo.setTagFlag(0x95, CDAFAILED); 131 | return success; 132 | } 133 | 134 | if (!repo.isTagExist(0x9F46)) 135 | { 136 | repo.setTagFlag(0x95, ICCDATAMISSING); 137 | repo.setTagFlag(0x95, CDAFAILED); 138 | return success; 139 | } 140 | 141 | if (!repo.isTagExist(0x9F47)) 142 | { 143 | repo.setTagFlag(0x95, ICCDATAMISSING); 144 | repo.setTagFlag(0x95, CDAFAILED); 145 | return success; 146 | } 147 | 148 | Tlv* tag90 = repo.getTag(0x90); 149 | 150 | len = tag90->len; 151 | if (pkModLen != len) 152 | { 153 | repo.setTagFlag(0x95, CDAFAILED); 154 | return success; 155 | } 156 | 157 | if (failure == secUtil.recoverPubKeyCert(PKCAMOD, pkModLen, exponent, 158 | iExpLen, len, tag90->val.data(), &issModLen)) 159 | { 160 | repo.setTagFlag(0x95, CDAFAILED); 161 | return success; 162 | } 163 | 164 | memcpy(repo.issPkModulus, repo.recPkModulus, issModLen); 165 | 166 | Tlv* tag9F46 = repo.getTag(0x9F46); 167 | len = tag9F46->len; 168 | if ((int)issModLen != len) 169 | { 170 | repo.setTagFlag(0x95, CDAFAILED); 171 | return success; 172 | } 173 | 174 | if (failure == secUtil.recoverICCPubKeyCert(issModLen, &repo.cdaIccPkModLen)) 175 | { 176 | repo.setTagFlag(0x95, CDAFAILED); 177 | return success; 178 | } 179 | 180 | return success; 181 | } 182 | 183 | int EmvL2OfflineDataAuth::performDDA() 184 | { 185 | int i, len, iExpLen, rv; 186 | uint8_t iccModLen, issModLen; 187 | uint8_t pkModLen; 188 | uint8_t exponent[3], lenLen, ddolLen; 189 | uint8_t sw1, sw2; 190 | uint8_t ddolData[DOLBUFFERLEN]; 191 | 192 | memset(repo.caPkModulus, 0, sizeof(repo.caPkModulus)); 193 | memset(repo.issPkModulus, 0, sizeof(repo.issPkModulus)); 194 | memset(ddolData, 0, sizeof(ddolData)); 195 | 196 | if (!repo.isTagExist(0x95) || !repo.isTagExist(0x9B)) 197 | { 198 | return emvMissingMandatoryDataError; 199 | } 200 | 201 | repo.setTagFlag(0x9B, OFFAUTHPERFORMED); 202 | if (!repo.isTagExist(0x8F)) 203 | { 204 | repo.setTagFlag(0x95, ICCDATAMISSING); 205 | repo.setTagFlag(0x95, DDAFAILED); 206 | return success; 207 | } 208 | 209 | if (failure == repo.searchCAKeys()) 210 | { 211 | repo.setTagFlag(0x95, DDAFAILED); 212 | return success; 213 | } 214 | 215 | pkModLen = repo.activeCAKey->ucPKModuloLen; 216 | memcpy(repo.caPkModulus, &repo.activeCAKey->ucPKModulo, pkModLen); 217 | iExpLen = repo.activeCAKey->ucPKExpLen; 218 | memcpy(exponent, repo.activeCAKey->ucPKExp, iExpLen); 219 | 220 | if (!repo.isTagExist(0x90)) 221 | { 222 | repo.setTagFlag(0x95, ICCDATAMISSING); 223 | repo.setTagFlag(0x95, DDAFAILED); 224 | return success; 225 | } 226 | 227 | if (!repo.isTagExist(0x9F32)) 228 | { 229 | repo.setTagFlag(0x95, ICCDATAMISSING); 230 | repo.setTagFlag(0x95, DDAFAILED); 231 | return success; 232 | } 233 | 234 | if (!repo.isTagExist(0x9F46)) 235 | { 236 | repo.setTagFlag(0x95, ICCDATAMISSING); 237 | repo.setTagFlag(0x95, DDAFAILED); 238 | return success; 239 | } 240 | 241 | if (!repo.isTagExist(0x9F47)) 242 | { 243 | repo.setTagFlag(0x95, ICCDATAMISSING); 244 | repo.setTagFlag(0x95, DDAFAILED); 245 | return success; 246 | } 247 | 248 | Tlv* tag90 = repo.getTag(0x90); 249 | 250 | len = tag90->len; 251 | if (pkModLen != len) 252 | { 253 | repo.setTagFlag(0x95, DDAFAILED); 254 | return success; 255 | } 256 | 257 | if (failure == secUtil.recoverPubKeyCert(PKCAMOD, pkModLen, exponent, 258 | iExpLen, len, tag90->val.data(), &issModLen)) 259 | { 260 | repo.setTagFlag(0x95, DDAFAILED); 261 | return success; 262 | } 263 | 264 | memcpy(repo.issPkModulus, repo.recPkModulus, issModLen); 265 | 266 | Tlv* tag9F46 = repo.getTag(0x9F46); 267 | 268 | len = tag9F46->len; 269 | if (issModLen != len) 270 | { 271 | repo.setTagFlag(0x95, DDAFAILED); 272 | return success; 273 | } 274 | 275 | if (failure == secUtil.recoverICCPubKeyCert(issModLen, &iccModLen)) 276 | { 277 | repo.setTagFlag(0x95, DDAFAILED); 278 | return success; 279 | } 280 | 281 | Tlv* tag9F49 = repo.getTag(0x9F49); 282 | 283 | if (!tag9F49) 284 | { 285 | Tlv* tagDF8B12 = repo.getTag(0xDF8B12); 286 | 287 | len = tagDF8B12->len; 288 | if (!len) 289 | { 290 | repo.setTagFlag(0x95, ICCDATAMISSING); 291 | repo.setTagFlag(0x95, DDAFAILED); 292 | return success; 293 | } 294 | 295 | for (i = 0; i < len - 1; i++) 296 | { 297 | if ((tagDF8B12->val.data()[i] == 0x9F) && 298 | (tagDF8B12->val.data()[i + 1] == 0x37)) 299 | { 300 | break; 301 | } 302 | } 303 | 304 | if (i == (len - 1)) 305 | { 306 | repo.setTagFlag(0x95, DDAFAILED); 307 | return success; 308 | } 309 | 310 | util.collectDolData(tagDF8B12->val.data(), len, ddolData, &ddolLen); 311 | } 312 | else 313 | { 314 | len = tag9F49->len; 315 | uint8_t* tmpBuff = tag9F49->val.data(); 316 | for (i = 0; i < len - 1; i++) 317 | { 318 | if ((tmpBuff[i] == 0x9F) && (tmpBuff[i + 1] == 0x37)) 319 | { 320 | break; 321 | } 322 | } 323 | 324 | if (i == (len - 1)) 325 | { 326 | repo.setTagFlag(0x95, DDAFAILED); 327 | return success; 328 | } 329 | 330 | util.collectDolData(tmpBuff, len, ddolData, &ddolLen); 331 | } 332 | 333 | if (repo.isTagFlag(0x95, DDAFAILED)) 334 | { 335 | return success; 336 | } 337 | 338 | rv = command.internalAuthenticate(ddolData, ddolLen); 339 | if (rv != success) 340 | { 341 | return rv; 342 | } 343 | 344 | sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 345 | sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 346 | 347 | if ((sw1 != 0x90) || (sw2 != 0x00)) 348 | { 349 | return cardRejected; 350 | } 351 | 352 | if (repo.apdu.rdata[0] == 0x80) 353 | { 354 | if ((repo.apdu.rdata[1] & 0x80) == 0x80) 355 | { 356 | len = repo.parseLen(&repo.apdu.rdata[1], &lenLen); 357 | } 358 | else 359 | { 360 | lenLen = 1; 361 | len = repo.apdu.rdata[1]; 362 | } 363 | 364 | if (len != repo.apdu.rlen - 3 - lenLen) 365 | { 366 | return emvLenError; 367 | } 368 | 369 | if (len == 0) 370 | { 371 | return emvMissingMandatoryDataError; 372 | } 373 | 374 | repo.setTag(0x9F4B, &repo.apdu.rdata[1]); 375 | } 376 | else if (repo.apdu.rdata[0] == 0x77) 377 | { 378 | len = repo.parseLen(&repo.apdu.rdata[1], &lenLen); 379 | rv = repo.parseTags(&repo.apdu.rdata[1 + lenLen], len); 380 | 381 | if (rv != success) 382 | { 383 | return rv; 384 | } 385 | 386 | if (!repo.isTagExist(0x9F4B)) 387 | { 388 | return emvMissingMandatoryDataError; 389 | } 390 | } 391 | else 392 | { 393 | return emvDataFormatError; 394 | } 395 | 396 | if (failure == secUtil.verifyDynamicSign(PKICCMOD, iccModLen, ddolData, ddolLen)) 397 | { 398 | repo.setTagFlag(0x95, DDAFAILED); 399 | return success; 400 | } 401 | 402 | return success; 403 | } 404 | 405 | int EmvL2OfflineDataAuth::performSDA() 406 | { 407 | int len, iExpLen; 408 | uint8_t issModLen, pkModLen; 409 | uint8_t exponent[3]; 410 | 411 | memset(repo.caPkModulus, 0, sizeof(repo.caPkModulus)); 412 | memset(repo.issPkModulus, 0, sizeof(repo.issPkModulus)); 413 | 414 | if (!repo.isTagExist(0x95) || !repo.isTagExist(0x9B)) 415 | { 416 | return emvMissingMandatoryDataError; 417 | } 418 | 419 | repo.setTagFlag(0x9B, OFFAUTHPERFORMED); 420 | 421 | if (!repo.isTagExist(0x8F)) 422 | { 423 | repo.setTagFlag(0x95, ICCDATAMISSING); 424 | repo.setTagFlag(0x95, SDAFAILED); 425 | return success; 426 | } 427 | 428 | if (failure == repo.searchCAKeys()) 429 | { 430 | repo.setTagFlag(0x95, SDAFAILED); 431 | return success; 432 | } 433 | 434 | pkModLen = repo.activeCAKey->ucPKModuloLen; 435 | memcpy(repo.caPkModulus, &repo.activeCAKey->ucPKModulo, pkModLen); 436 | iExpLen = repo.activeCAKey->ucPKExpLen; 437 | memcpy(exponent, repo.activeCAKey->ucPKExp, iExpLen); 438 | 439 | if (!repo.isTagExist(0x90)) 440 | { 441 | repo.setTagFlag(0x95, ICCDATAMISSING); 442 | repo.setTagFlag(0x95, SDAFAILED); 443 | return success; 444 | } 445 | 446 | if (!repo.isTagExist(0x9F32)) 447 | { 448 | repo.setTagFlag(0x95, ICCDATAMISSING); 449 | repo.setTagFlag(0x95, SDAFAILED); 450 | return success; 451 | } 452 | 453 | if (!repo.isTagExist(0x93)) 454 | { 455 | repo.setTagFlag(0x95, ICCDATAMISSING); 456 | repo.setTagFlag(0x95, SDAFAILED); 457 | return success; 458 | } 459 | 460 | Tlv* tag90 = repo.getTag(0x90); 461 | if (!tag90 || pkModLen != tag90->len) 462 | { 463 | repo.setTagFlag(0x95, SDAFAILED); 464 | return success; 465 | } 466 | len = tag90->len; 467 | 468 | if (failure == secUtil.recoverPubKeyCert(PKCAMOD, pkModLen, exponent, iExpLen, len, tag90->val.data(), &issModLen)) 469 | { 470 | repo.setTagFlag(0x95, SDAFAILED); 471 | return success; 472 | } 473 | 474 | memcpy(repo.issPkModulus, repo.recPkModulus, issModLen); 475 | if (failure == verifyStaticAppData(PKISSMOD, issModLen)) 476 | { 477 | repo.setTagFlag(0x95, SDAFAILED); 478 | return success; 479 | } 480 | 481 | return success; 482 | } 483 | 484 | int EmvL2OfflineDataAuth::verifyStaticAppData(emvl2PkMode type, uint8_t modLen) 485 | { 486 | int len; 487 | int ret = success; 488 | 489 | Tlv* tag93 = repo.getTag(0x93); 490 | len = tag93->len; 491 | if ((uint8_t)len != modLen) 492 | { 493 | ret = failure; 494 | } 495 | else if (failure == recoverStaticAppData(type, modLen)) 496 | { 497 | ret = failure; 498 | } 499 | 500 | return ret; 501 | } 502 | 503 | int EmvL2OfflineDataAuth::recoverStaticAppData(emvl2PkMode type, uint8_t modLen) 504 | { 505 | int hashIdx = 0, sdataLen; 506 | uint8_t digest[20]; 507 | uint8_t* hashData; 508 | uint16_t lenHashData = 0; 509 | 510 | memset(repo.recoveredData, 0, sizeof(repo.recoveredData)); 511 | 512 | Tlv* tag9F32 = repo.getTag(0x9F32); 513 | Tlv* tag93 = repo.getTag(0x93); 514 | 515 | secUtil.rsaEncrypt(type, modLen, tag9F32->val.data(), tag9F32->len, tag93->val.data(), tag93->len); 516 | 517 | if (repo.recoveredData[0] != 0x6A) 518 | { 519 | return failure; 520 | } 521 | 522 | if (repo.recoveredData[1] != 0x03) 523 | { 524 | return failure; 525 | } 526 | 527 | if (repo.recoveredData[2] != 0x01) 528 | { 529 | return failure; 530 | } 531 | 532 | if (repo.recoveredData[modLen - 1] != 0xBC) 533 | { 534 | return failure; 535 | } 536 | 537 | lenHashData = 4 + (modLen - 26) + (uint16_t)repo.staticAppData.size(); 538 | hashData = (uint8_t*)malloc(lenHashData); 539 | if (NULL == hashData) 540 | { 541 | return failure; 542 | } 543 | 544 | repo.setTag(0x9F45, &repo.recoveredData[3], 2); 545 | 546 | memcpy(hashData + hashIdx, repo.recoveredData + 1, 4 + (modLen - 26)); 547 | hashIdx += 4 + (modLen - 26); 548 | memcpy(hashData + hashIdx, repo.staticAppData.data(), repo.staticAppData.size()); 549 | hashIdx += repo.staticAppData.size(); 550 | 551 | Tlv* tag9F4A = repo.getTag(0x9F4A); 552 | if (tag9F4A) 553 | { 554 | if ((tag9F4A->val.data()[0] != 0x82) || (tag9F4A->len != 0x01)) 555 | { 556 | return failure; 557 | } 558 | 559 | secUtil.prepStaticTagListData(&sdataLen); 560 | if (sdataLen == 0) 561 | { 562 | return failure; 563 | } 564 | hashData = (uint8_t*)realloc(hashData, lenHashData + sdataLen); 565 | if (NULL == hashData) 566 | { 567 | return failure; 568 | } 569 | 570 | memcpy(hashData + hashIdx, repo.sdaTermData, sdataLen); 571 | hashIdx += sdataLen; 572 | } 573 | 574 | device.sha1(hashData, hashIdx, digest); 575 | free(hashData); 576 | if (memcmp(digest, repo.recoveredData + modLen - 21, sizeof(digest)) != 0) 577 | { 578 | return failure; 579 | } 580 | 581 | return success; 582 | } 583 | 584 | -------------------------------------------------------------------------------- /corelib/emvl2OfflineDataAuth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Command.h" 6 | #include "emvl2Util.h" 7 | #include "emvl2SecUtil.h" 8 | 9 | using namespace std; 10 | 11 | class EmvL2OfflineDataAuth { 12 | public: 13 | EmvL2OfflineDataAuth(IDevice& device); 14 | ~EmvL2OfflineDataAuth(); 15 | uint8_t perform(); 16 | 17 | private: 18 | int performCDA(); 19 | int performDDA(); 20 | int performSDA(); 21 | int verifyStaticAppData(emvl2PkMode type, uint8_t modLen); 22 | int recoverStaticAppData(emvl2PkMode type, uint8_t modLen); 23 | 24 | private: 25 | IDevice& device; 26 | EmvL2Repo& repo; 27 | EmvL2Command& command; 28 | EmvL2Util& util; 29 | EmvL2SecUtil& secUtil; 30 | }; 31 | -------------------------------------------------------------------------------- /corelib/emvl2ProcessCVM.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Command.h" 6 | #include "emvl2Util.h" 7 | #include "emvl2SecUtil.h" 8 | 9 | using namespace std; 10 | 11 | class EmvL2ProcessCVM { 12 | public: 13 | EmvL2ProcessCVM(IDevice& device); 14 | ~EmvL2ProcessCVM(); 15 | uint8_t perform(); 16 | 17 | private: 18 | int processResult(uint8_t result, uint8_t condCode, uint8_t code); 19 | uint8_t init(); 20 | void finalize(int idx); 21 | void nextCVMCode(int idx, uint8_t* code, uint8_t* type); 22 | uint8_t checkConditionCode(uint8_t condCode, uint8_t code); 23 | uint8_t isUnattendedTerminal(uint8_t ttype); 24 | uint8_t cvmIsSupported(uint8_t code); 25 | uint8_t isCVMCodeSupported(uint8_t code, int* isSuccess); 26 | uint8_t doMethod(uint8_t type, int* isSuccess); 27 | int offlinePlainPIN(int* isSuccess); 28 | int offlineEncryptedPIN(int* isSuccess); 29 | int getICCEncPublicKey(uint8_t* modLen); 30 | int getICCPublicKey(uint8_t* modLen); 31 | 32 | private: 33 | IDevice& device; 34 | EmvL2Repo& repo; 35 | EmvL2Command& command; 36 | EmvL2Util& util; 37 | EmvL2SecUtil& secUtil; 38 | }; 39 | -------------------------------------------------------------------------------- /corelib/emvl2ProcessRestrict.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2ProcessRestrict.h" 2 | 3 | EmvL2ProcessRestrict::EmvL2ProcessRestrict(IDevice& device) : device(device), repo(EmvL2Repo::getInstance()), command(EmvL2Command::getInstance()), util(EmvL2Util::getInstance()) { 4 | } 5 | 6 | EmvL2ProcessRestrict::~EmvL2ProcessRestrict() { 7 | 8 | } 9 | 10 | uint8_t EmvL2ProcessRestrict::perform() { 11 | Tlv* tag9F09 = repo.getTag(0x9F09); 12 | Tlv* tag9F08 = repo.getTag(0x9F08); 13 | 14 | if (repo.isTagExist(0x9F09) && (memcmp(tag9F09->val.data(), tag9F08->val.data(), tag9F08->len) != 0)) 15 | { 16 | repo.setTagFlag(0x95, MISSMATCHAPPVERSIONS); 17 | } 18 | 19 | if (repo.isTagExist(0x9F07)) 20 | { 21 | Tlv* tag9F1A = repo.getTag(0x9F1A); 22 | Tlv* tag5F28 = repo.getTag(0x5F28); 23 | 24 | if (tag9F1A && tag5F28) 25 | { 26 | uint8_t ttype = repo.getTag(0x9C)->val.data()[0]; 27 | if (memcmp(tag9F1A->val.data(), tag5F28->val.data(), tag5F28->len) == 0) 28 | { 29 | if (ttype == TRNCASH) 30 | { 31 | if (!repo.isTagFlag(0x9F40, ACCASH)) 32 | { 33 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 34 | } 35 | 36 | if (!repo.isTagFlag(0x9F07, DOMESTICCASHVALID)) 37 | { 38 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 39 | } 40 | } 41 | else if (ttype == TRNSALE) 42 | { 43 | if (!repo.isTagFlag(0x9F40, ACGOODS)) 44 | { 45 | if (!repo.isTagFlag(0x9F40, ACSERVICES)) 46 | { 47 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 48 | } 49 | } 50 | 51 | if (!repo.isTagFlag(0x9F07, DOMESTICGOODSVALID)) 52 | { 53 | if (!repo.isTagFlag(0x9F07, DOMESTICSERVICESVALID)) 54 | { 55 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 56 | } 57 | } 58 | } 59 | else if (ttype == TRNCASHBACK) 60 | { 61 | if (!repo.isTagFlag(0x9F40, ACCASHBACK)) 62 | { 63 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 64 | } 65 | 66 | if (!repo.isTagFlag(0x9F07, DOMESTICCASHBACKALLOWED)) 67 | { 68 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 69 | } 70 | } 71 | } 72 | else 73 | { 74 | if (ttype == TRNCASH) 75 | { 76 | if (!repo.isTagFlag(0x9F40, ACCASH)) 77 | { 78 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 79 | } 80 | 81 | if (!repo.isTagFlag(0x9F07, INTERNATIONALCASHVALID)) 82 | { 83 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 84 | } 85 | } 86 | else if (ttype == TRNSALE) 87 | { 88 | if (!repo.isTagFlag(0x9F40, ACGOODS)) 89 | { 90 | if (!repo.isTagFlag(0x9F40, ACSERVICES)) 91 | { 92 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 93 | } 94 | } 95 | 96 | if (!repo.isTagFlag(0x9F07, INTERNATIONALGOODSVALID)) 97 | { 98 | if (!repo.isTagFlag(0x9F07, INTERNATIONALSERVICESVALID)) 99 | { 100 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 101 | } 102 | } 103 | } 104 | else if (ttype == TRNCASHBACK) 105 | { 106 | if (!repo.isTagFlag(0x9F40, ACCASHBACK)) 107 | { 108 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 109 | } 110 | 111 | if (!repo.isTagFlag(0x9F07, INTERCASHBACKALLOWED)) 112 | { 113 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 114 | } 115 | } 116 | } 117 | } 118 | 119 | Tlv* tag9F35 = repo.getTag(0x9F35); 120 | 121 | if (tag9F35->val.data()[0] == 0x15 || tag9F35->val.data()[0] == 0x14) 122 | { 123 | if (!repo.isTagFlag(0x9F07, ATMSVALID)) 124 | { 125 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 126 | } 127 | } 128 | else 129 | { 130 | if (!repo.isTagFlag(0x9F07, NONATMSVALID)) 131 | { 132 | repo.setTagFlag(0x95, SERVICENOTALLOWED); 133 | } 134 | } 135 | } 136 | 137 | int intCurrentDate = 0; 138 | uint8_t* currentDate = repo.getTag(0x9A)->val.data(); 139 | 140 | int intEffDate = 0; 141 | uint8_t* appEffDate = repo.getTag(0x5F25)->val.data(); 142 | if (appEffDate[0] < 0x50) 143 | { 144 | intEffDate = appEffDate[0] + 0x100; 145 | } 146 | else 147 | { 148 | intEffDate = (int)appEffDate[0]; 149 | } 150 | 151 | if (currentDate[0] < 0x50) 152 | { 153 | intCurrentDate = (int)currentDate[0] + 0x100; 154 | } 155 | else 156 | { 157 | intCurrentDate = (int)currentDate[0]; 158 | } 159 | 160 | if (intEffDate > intCurrentDate) 161 | { 162 | repo.setTagFlag(0x95, APPNOTYETEFFECTIVE); 163 | } 164 | else if (intEffDate == intCurrentDate) 165 | { 166 | if (appEffDate[1] > currentDate[1]) 167 | { 168 | repo.setTagFlag(0x95, APPNOTYETEFFECTIVE); 169 | } 170 | 171 | if (appEffDate[1] == currentDate[1]) 172 | { 173 | if (appEffDate[2] > currentDate[2]) 174 | { 175 | repo.setTagFlag(0x95, APPNOTYETEFFECTIVE); 176 | } 177 | } 178 | } 179 | 180 | int intExpDate = 0; 181 | uint8_t* appExpDate = repo.getTag(0x5F24)->val.data(); 182 | if (appExpDate[0] < 0x50) 183 | { 184 | intExpDate = (int)appExpDate[0] + 0x100; 185 | } 186 | else 187 | { 188 | intExpDate = (int)appExpDate[0]; 189 | } 190 | 191 | if (currentDate[0] < 0x50) 192 | { 193 | intCurrentDate = (int)currentDate[0] + 0x100; 194 | } 195 | else 196 | { 197 | intCurrentDate = (int)currentDate[0]; 198 | } 199 | 200 | if (intExpDate < intCurrentDate) 201 | { 202 | repo.setTagFlag(0x95, EXPIREDAPP); 203 | } 204 | 205 | if (intExpDate == intCurrentDate) 206 | { 207 | if (appExpDate[1] < currentDate[1]) 208 | { 209 | repo.setTagFlag(0x95, EXPIREDAPP); 210 | } 211 | 212 | if (appExpDate[1] == currentDate[1]) 213 | { 214 | if (appExpDate[2] < currentDate[2]) 215 | { 216 | repo.setTagFlag(0x95, EXPIREDAPP); 217 | } 218 | } 219 | } 220 | 221 | return success; 222 | } -------------------------------------------------------------------------------- /corelib/emvl2ProcessRestrict.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Command.h" 6 | #include "emvl2Util.h" 7 | 8 | using namespace std; 9 | 10 | class EmvL2ProcessRestrict { 11 | public: 12 | EmvL2ProcessRestrict(IDevice& device); 13 | ~EmvL2ProcessRestrict(); 14 | uint8_t perform(); 15 | 16 | private: 17 | IDevice& device; 18 | EmvL2Repo& repo; 19 | EmvL2Command& command; 20 | EmvL2Util& util; 21 | }; 22 | -------------------------------------------------------------------------------- /corelib/emvl2ReadAppData.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2ReadAppData.h" 2 | #include "emvl2Command.h" 3 | #include "emvl2TagDef.h" 4 | 5 | EmvL2ReadAppData::EmvL2ReadAppData(IDevice& device) : device(device), repo(EmvL2Repo::getInstance()), command(EmvL2Command::getInstance()) 6 | , util(EmvL2Util::getInstance()), secUtil(EmvL2SecUtil::getInstance()) { 7 | } 8 | 9 | EmvL2ReadAppData::~EmvL2ReadAppData() { 10 | 11 | } 12 | 13 | uint8_t EmvL2ReadAppData::perform() { 14 | uint8_t sw1, sw2, lenLen, authType, rv = failure; 15 | 16 | Tlv* tag94 = repo.getTag(0x94); 17 | if (!tag94 || tag94->len < 4) 18 | { 19 | return aflLenError; 20 | } 21 | 22 | int aflIdx = 0; 23 | while (aflIdx < (int)tag94->len) 24 | { 25 | uint8_t* aflData = &tag94->val.data()[aflIdx]; 26 | aflIdx += 4; 27 | uint8_t sfiVal = aflData[0]; 28 | sfiVal &= 0xF8; 29 | uint8_t realSfiVal = (sfiVal >> 3) & 0x1F; 30 | uint8_t firstRecNo = aflData[1]; 31 | uint8_t lastRecNo = aflData[2]; 32 | int offCnt = aflData[3]; 33 | 34 | if (realSfiVal == 0 || realSfiVal > 30) 35 | { 36 | return sfiLenError; 37 | } 38 | 39 | if (firstRecNo == 0 || firstRecNo > lastRecNo || offCnt > lastRecNo - firstRecNo + 1) 40 | { 41 | return aflDataError; 42 | } 43 | 44 | for (int i = firstRecNo; i <= lastRecNo; i++) 45 | { 46 | int readLen = 0; 47 | int readIdx = 0; 48 | rv = command.readRecord(sfiVal, (uint8_t)i); 49 | if (rv != success) 50 | { 51 | return rv; 52 | } 53 | 54 | sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 55 | sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 56 | 57 | if ((sw1 != 0x90) || (sw2 != 0)) 58 | { 59 | return cardRejected; 60 | } 61 | 62 | while ((repo.apdu.rdata[readIdx] == 0) || (repo.apdu.rdata[readIdx] == 0xFF)) 63 | { 64 | readIdx++; 65 | } 66 | 67 | if ((repo.apdu.rdata[readIdx] != 0x70) && ((realSfiVal >= 1) && (realSfiVal <= 10))) 68 | { 69 | return emvDataFormatError; 70 | } 71 | else if ((repo.apdu.rdata[readIdx] != 0x70) && ((realSfiVal >= 11) && (realSfiVal <= 30)) && offCnt != 0) 72 | { 73 | secUtil.determineDataAuthType(&authType); 74 | 75 | lenLen = repo.apdu.rdata[++readIdx] & 0x7F; 76 | readLen = util.bin2Int(&repo.apdu.rdata[readIdx + 1], lenLen); 77 | readIdx += lenLen + 1; 78 | 79 | switch (authType) 80 | { 81 | case ASDA: 82 | repo.isTagFlag(0x95, SDAFAILED); 83 | break; 84 | 85 | case ADDA: 86 | repo.isTagFlag(0x95, DDAFAILED); 87 | break; 88 | 89 | case ACDA: 90 | repo.isTagFlag(0x95, CDAFAILED); 91 | break; 92 | default: 93 | break; 94 | } 95 | } 96 | else 97 | { 98 | if (repo.apdu.rdata[readIdx] == 0x70) 99 | { 100 | readIdx += 1; 101 | 102 | if ((repo.apdu.rdata[readIdx] & 0x80) == 0x80) 103 | { 104 | lenLen = repo.apdu.rdata[readIdx] & 0x7F; 105 | readLen = util.bin2Int(&repo.apdu.rdata[readIdx + 1], lenLen); 106 | readIdx += lenLen + 1; 107 | } 108 | else 109 | { 110 | readLen = repo.apdu.rdata[readIdx]; 111 | readIdx++; 112 | } 113 | 114 | if ((readIdx + readLen) != (repo.apdu.rlen - 2)) 115 | { 116 | return emvDataFormatError; 117 | } 118 | } 119 | } 120 | 121 | rv = repo.parseTags(&repo.apdu.rdata[readIdx], readLen); 122 | if ((rv != success) && ((rv != noTag))) 123 | { 124 | if ((rv == emvDataFormatError) && ((realSfiVal >= 11) && (realSfiVal <= 30)) && offCnt != 0) 125 | { 126 | secUtil.determineDataAuthType(&authType); 127 | 128 | switch (authType) 129 | { 130 | case ASDA: 131 | repo.isTagFlag(0x95, SDAFAILED); 132 | break; 133 | 134 | case ADDA: 135 | repo.isTagFlag(0x95, DDAFAILED); 136 | break; 137 | 138 | case ACDA: 139 | repo.isTagFlag(0x95, CDAFAILED); 140 | break; 141 | default: 142 | break; 143 | } 144 | } 145 | else 146 | { 147 | return rv; 148 | } 149 | } 150 | 151 | if (offCnt != 0) 152 | { 153 | offCnt--; 154 | 155 | if ((realSfiVal >= 1) && (realSfiVal <= 10)) 156 | { 157 | repo.staticAppData.insert(repo.staticAppData.end(), &repo.apdu.rdata[readIdx], &repo.apdu.rdata[readIdx] + readLen); 158 | } 159 | else if ((realSfiVal >= 11) && (realSfiVal <= 30)) 160 | { 161 | repo.staticAppData.insert(repo.staticAppData.end(), &repo.apdu.rdata[0], &repo.apdu.rdata[0] + repo.apdu.rlen - 2); 162 | } 163 | } 164 | } 165 | } 166 | 167 | if (!checkMandatoryTags()) { 168 | return emvMissingMandatoryDataError; 169 | } 170 | 171 | Tlv* tag5F24 = repo.getTag(0x5F24); 172 | if (!tag5F24 || (failure == isDateValid(tag5F24->val.data()))) 173 | { 174 | return expDateFormatError; 175 | } 176 | 177 | Tlv* tag5F25 = repo.getTag(0x5F25); 178 | if (!tag5F25 || (failure == isDateValid(tag5F25->val.data()))) 179 | { 180 | return effDateFormatError; 181 | } 182 | 183 | return success; 184 | } 185 | 186 | uint8_t EmvL2ReadAppData::isDateValid(uint8_t* Date) 187 | { 188 | if (Date[1] == 0x00 || Date[1] > 0x12 || Date[2] == 0x00 || Date[2] > 0x31) 189 | { 190 | return failure; 191 | } 192 | else 193 | { 194 | if ((util.bcd2Int(Date, 1) % 4) == 0) 195 | { 196 | if ((Date[1] == 0x02) && (Date[2] > 0x29)) 197 | { 198 | return failure; 199 | } 200 | } 201 | else 202 | { 203 | if ((Date[1] == 0x02) && (Date[2] > 0x28)) 204 | { 205 | return failure; 206 | } 207 | } 208 | } 209 | 210 | return success; 211 | } 212 | 213 | bool EmvL2ReadAppData::checkMandatoryTags() { 214 | if (!repo.isTagExist(0x5A) || !repo.isTagExist(0x82) || !repo.isTagExist(0x8C) || !repo.isTagExist(0x8D) || !repo.isTagExist(0x5F24)) 215 | return false; 216 | 217 | return true; 218 | } -------------------------------------------------------------------------------- /corelib/emvl2ReadAppData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Command.h" 6 | #include "emvl2Util.h" 7 | #include "emvl2SecUtil.h" 8 | 9 | using namespace std; 10 | 11 | class EmvL2ReadAppData { 12 | public: 13 | EmvL2ReadAppData(IDevice& device); 14 | ~EmvL2ReadAppData(); 15 | uint8_t perform(); 16 | 17 | private: 18 | uint8_t isDateValid(uint8_t* Date); 19 | bool checkMandatoryTags(); 20 | 21 | private: 22 | IDevice& device; 23 | EmvL2Repo& repo; 24 | EmvL2Command& command; 25 | EmvL2Util& util; 26 | EmvL2SecUtil& secUtil; 27 | }; 28 | -------------------------------------------------------------------------------- /corelib/emvl2Repo.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2Repo.h" 2 | #include "emvl2Util.h" 3 | #include 4 | 5 | EmvL2Repo* EmvL2Repo::instance = NULL; 6 | 7 | EmvL2Repo& EmvL2Repo::getInstance() { 8 | if (!instance) { 9 | instance = new EmvL2Repo(); 10 | } 11 | return *instance; 12 | } 13 | 14 | EmvL2Repo::EmvL2Repo() { 15 | device = NULL; 16 | } 17 | 18 | EmvL2Repo::~EmvL2Repo() { 19 | 20 | } 21 | 22 | emvl2Ret EmvL2Repo::init(IDevice* device) { 23 | this->device = device; 24 | return success; 25 | } 26 | 27 | int EmvL2Repo::addCaKey(emvl2CAKey key) { 28 | this->caKeys.push_back(key); 29 | return success; 30 | } 31 | 32 | int EmvL2Repo::addAidPrms(emvl2AIDPrms prms) { 33 | this->aidPrms.push_back(prms); 34 | return success; 35 | } 36 | 37 | void EmvL2Repo::clearCaKeys() { 38 | this->caKeys.clear(); 39 | } 40 | 41 | void EmvL2Repo::clearAidPrms() { 42 | this->aidPrms.clear(); 43 | } 44 | 45 | void EmvL2Repo::clearApdu() { 46 | memset(&apdu, 0, sizeof(emvl2Apdu)); 47 | } 48 | 49 | int EmvL2Repo::searchCAKeys(void) 50 | { 51 | uint32_t dataLen = 0; 52 | uint16_t index = 0; 53 | int ret = success; 54 | 55 | Tlv* tag8F = getTag(0x8F); 56 | Tlv* tag84 = getTag(0x84); 57 | if (!tag8F || !tag84) 58 | { 59 | setTagFlag(0x95, ICCDATAMISSING); 60 | ret = failure; 61 | } 62 | 63 | if (failure != ret) 64 | { 65 | ret = failure; 66 | for (const emvl2CAKey& cakey : caKeys) { 67 | if (!memcmp(cakey.ucRid, tag84->val.data(), 5) && cakey.ucPKIndex == tag8F->val.data()[0]) { 68 | ret = success; 69 | activeCAKey = (emvl2CAKey*) & cakey; 70 | break; 71 | } 72 | } 73 | } 74 | 75 | if (failure != ret) 76 | { 77 | setTag(0x9F22, tag8F->val.data(), 1); 78 | } 79 | 80 | return ret; 81 | } 82 | 83 | void EmvL2Repo::clearCardTags() { 84 | tags.erase(0x42); 85 | tags.erase(0x4F); 86 | tags.erase(0x50); 87 | tags.erase(0x57); 88 | tags.erase(0x58); 89 | tags.erase(0x5A); 90 | tags.erase(0x5F20); 91 | tags.erase(0x5F24); 92 | tags.erase(0x5F25); 93 | tags.erase(0x5F28); 94 | tags.erase(0x5F2D); 95 | tags.erase(0x5F30); 96 | tags.erase(0x5F34); 97 | tags.erase(0x5F55); 98 | tags.erase(0x5F56); 99 | tags.erase(0x6F); 100 | tags.erase(0x71); 101 | tags.erase(0x72); 102 | tags.erase(0x73); 103 | tags.erase(0x82); 104 | tags.erase(0x84); 105 | tags.erase(0x87); 106 | tags.erase(0x88); 107 | tags.erase(0x8C); 108 | tags.erase(0x8D); 109 | tags.erase(0x8E); 110 | tags.erase(0x8F); 111 | tags.erase(0x90); 112 | tags.erase(0x91); 113 | tags.erase(0x92); 114 | tags.erase(0x93); 115 | tags.erase(0x94); 116 | tags.erase(0x97); 117 | tags.erase(0x9F05); 118 | tags.erase(0x9F07); 119 | tags.erase(0x9F08); 120 | tags.erase(0x9F0B); 121 | tags.erase(0x9F0D); 122 | tags.erase(0x9F0E); 123 | tags.erase(0x9F0F); 124 | tags.erase(0x9F10); 125 | tags.erase(0x9F11); 126 | tags.erase(0x9F12); 127 | tags.erase(0x9F13); 128 | tags.erase(0x9F14); 129 | tags.erase(0x9F17); 130 | tags.erase(0x9F19); 131 | tags.erase(0x9F1F); 132 | tags.erase(0x9F20); 133 | tags.erase(0x9F23); 134 | tags.erase(0x9F24); 135 | tags.erase(0x9F25); 136 | tags.erase(0x9F26); 137 | tags.erase(0x9F27); 138 | tags.erase(0x9F2D); 139 | tags.erase(0x9F2E); 140 | tags.erase(0x9F2F); 141 | tags.erase(0x9F30); 142 | tags.erase(0x9F32); 143 | tags.erase(0x9F36); 144 | tags.erase(0x9F38); 145 | tags.erase(0x9F3B); 146 | tags.erase(0x9F43); 147 | tags.erase(0x9F42); 148 | tags.erase(0x9F44); 149 | tags.erase(0x9F45); 150 | tags.erase(0x9F46); 151 | tags.erase(0x9F47); 152 | tags.erase(0x9F48); 153 | tags.erase(0x9F49); 154 | tags.erase(0x9F4A); 155 | tags.erase(0x9F4B); 156 | tags.erase(0x9F4C); 157 | tags.erase(0x9F4D); 158 | tags.erase(0xBF0C); 159 | } 160 | 161 | int EmvL2Repo::parseTags(uint8_t* buff, int buffLen) { 162 | int idx = 0, len, iIssScriptLen; 163 | uint8_t ucLenOfLen = 0, lenOfTag = 0; 164 | uint8_t indexOfTag = 0; 165 | uint32_t valueOfTag = 0; 166 | 167 | while (idx < buffLen) 168 | { 169 | if ((buff[idx] == 0) || (buff[idx] == 0xFF)) 170 | { 171 | idx++; 172 | continue; 173 | } 174 | 175 | if (buff[idx] == 0x71) 176 | { 177 | if (buff[idx + 1] != 0x81) { 178 | iIssScriptLen = buff[idx + 1]; 179 | 180 | script71.reserve(iIssScriptLen + 2); 181 | std::copy(&buff[idx], &buff[idx + iIssScriptLen + 2], std::back_inserter(script71)); 182 | 183 | idx += iIssScriptLen + 2; 184 | 185 | } 186 | else { 187 | iIssScriptLen = buff[idx + 2]; 188 | 189 | script71.reserve(iIssScriptLen + 3); 190 | std::copy(&buff[idx], &buff[idx + iIssScriptLen + 3], std::back_inserter(script71)); 191 | 192 | idx += iIssScriptLen + 3; 193 | } 194 | 195 | continue; 196 | } 197 | 198 | if (buff[idx] == 0x72) 199 | { 200 | if (buff[idx + 1] != 0x81) { 201 | iIssScriptLen = buff[idx + 1]; 202 | 203 | script72.reserve(iIssScriptLen + 2); 204 | std::copy(&buff[idx], &buff[idx + iIssScriptLen + 2], std::back_inserter(script72)); 205 | 206 | idx += iIssScriptLen + 2; 207 | 208 | } 209 | else { 210 | iIssScriptLen = buff[idx + 2]; 211 | 212 | script72.reserve(iIssScriptLen + 3); 213 | std::copy(&buff[idx], &buff[idx + iIssScriptLen + 3], std::back_inserter(script72)); 214 | 215 | idx += iIssScriptLen + 3; 216 | } 217 | 218 | continue; 219 | } 220 | 221 | if (buff[idx] == 0xA5) 222 | { 223 | idx++; 224 | len = parseLen(&buff[idx], &ucLenOfLen); 225 | idx += ucLenOfLen; 226 | continue; 227 | } 228 | 229 | lenOfTag = parseTag(&buff[idx], &valueOfTag); 230 | idx += lenOfTag; 231 | 232 | len = parseLen(&buff[idx], &ucLenOfLen); 233 | idx += ucLenOfLen; 234 | 235 | setTag(valueOfTag, &buff[idx], len); 236 | idx += len; 237 | } 238 | 239 | if (idx > buffLen) 240 | { 241 | return emvDataFormatError; 242 | } 243 | 244 | return success; 245 | } 246 | 247 | Tlv* EmvL2Repo::setTag(uint32_t tag, uint8_t* src, uint8_t len) { 248 | tags.erase(tag); 249 | if (src != NULL) { 250 | if (len == 0) { 251 | uint8_t lenLen = 0; 252 | len = parseLen(src, &lenLen); 253 | 254 | uint8_t* data = src + lenLen; 255 | 256 | Tlv tlv = { tag, len, vector(data, data + len) }; 257 | tags[tag] = tlv; 258 | } 259 | else { 260 | Tlv tlv = { tag, len, vector(src, src + len) }; 261 | tags[tag] = tlv; 262 | } 263 | } 264 | else { 265 | if (len > 0) { 266 | Tlv tlv = { tag, len, vector(len) }; 267 | tags[tag] = tlv; 268 | } 269 | } 270 | 271 | return getTag(tag); 272 | } 273 | 274 | Tlv* EmvL2Repo::getTag(uint32_t tag) { 275 | auto it = tags.find(tag); 276 | if (it != tags.end()) { 277 | return &tags[tag]; 278 | } 279 | 280 | return NULL; 281 | } 282 | 283 | int EmvL2Repo::getTagFormat(uint32_t tag) { 284 | switch (tag) { 285 | case 0x73: 286 | case 0x9A: 287 | case 0x9C: 288 | case 0x5F24: 289 | case 0x5F25: 290 | case 0x5F28: 291 | case 0x5F2A: 292 | case 0x5F30: 293 | case 0x5F34: 294 | case 0x5F36: 295 | case 0x9F01: 296 | case 0x9F02: 297 | case 0x9F03: 298 | case 0x9F15: 299 | case 0x9F1A: 300 | case 0x9F21: 301 | case 0x9F35: 302 | case 0x9F39: 303 | case 0x9F3B: 304 | case 0x9F3C: 305 | case 0x9F3D: 306 | case 0x9F41: 307 | case 0x9F42: 308 | case 0x9F43: 309 | case 0x9F44: 310 | case 0x9F4D: 311 | return FNUM; 312 | } 313 | 314 | switch (tag) { 315 | case 0x58: 316 | case 0x5A: 317 | case 0x9F20: 318 | return FCNM; 319 | } 320 | 321 | return FNULL; 322 | } 323 | 324 | bool EmvL2Repo::isTagExist(uint32_t tag) { 325 | auto it = tags.find(tag); 326 | if (it == tags.end()) 327 | return false; 328 | return true; 329 | } 330 | 331 | void EmvL2Repo::setTagFlag(uint32_t tag, int flag) { 332 | int i, j; 333 | uint8_t* data = getTagDatas(tag, &i, &j, flag); 334 | 335 | if(data) 336 | data[i] |= j; 337 | } 338 | 339 | bool EmvL2Repo::isTagFlag(uint32_t tag, int flag) { 340 | int i, j; 341 | uint8_t* data = getTagDatas(tag, &i, &j, flag); 342 | 343 | if (data) 344 | return (data[i] & j) ? true : false; 345 | 346 | return false; 347 | } 348 | 349 | uint32_t EmvL2Repo::parseLen(uint8_t *data, uint8_t* lenLen) 350 | { 351 | uint32_t len, lLen; 352 | 353 | if ((data[0] & 0x80) == 0x80) 354 | { 355 | lLen = data[0] & 0x7F; 356 | len = EmvL2Util::getInstance().bin2Int(&data[1], lLen); 357 | (lLen)++; 358 | } 359 | else 360 | { 361 | lLen = 1; 362 | len = (uint32_t)data[0]; 363 | } 364 | 365 | if (lenLen) 366 | *lenLen = lLen; 367 | 368 | return len; 369 | } 370 | 371 | uint32_t EmvL2Repo::parseTag(uint8_t* data, uint8_t* tagLen) 372 | { 373 | uint8_t tLen = 1; 374 | uint32_t tag = 0; 375 | int i = 0; 376 | 377 | tag = data[i]; 378 | if ((data[i++] & 0x1F) == 0x1F) { 379 | tLen++; 380 | tag = (tag << 8) | data[i]; 381 | while ((data[i++] & 0x80) == 0x80) { 382 | tLen++; 383 | tag = (tag << 8) | data[i]; 384 | } 385 | } 386 | 387 | if (tagLen) 388 | *tagLen = tLen; 389 | 390 | return tag; 391 | } 392 | 393 | void EmvL2Repo::nextTag(uint8_t* data, int* idx) 394 | { 395 | int len; 396 | uint8_t ch = 0x00, ucLenOfLen; 397 | int index = (*idx); 398 | 399 | while (ch == 0x00) 400 | { 401 | ch = data[index++]; 402 | } 403 | 404 | if (index - 1 > *idx) 405 | { 406 | (*idx) = index - 1; 407 | } 408 | else 409 | { 410 | ch = data[*idx] & 0x1F; 411 | 412 | if (ch == 0x1F) 413 | { 414 | (*idx)++; 415 | 416 | while ((data[*idx] & 0xF0) == 0xF0 || (data[*idx] & 0x81) == 0x81) 417 | { 418 | (*idx)++; 419 | } 420 | } 421 | 422 | (*idx)++; 423 | len = parseLen(&data[*idx], &ucLenOfLen); 424 | (*idx) += (int)ucLenOfLen; 425 | (*idx) += len; 426 | } 427 | } 428 | 429 | uint8_t* EmvL2Repo::getTagDatas(uint32_t tag, int *i, int *j, int flag) { 430 | if (i && j) { 431 | *i = flag / 0x100; 432 | *j = flag - (*i) * 0x100; 433 | } 434 | 435 | Tlv* tagData = getTag(tag); 436 | if (tagData) { 437 | return tagData->val.data(); 438 | } 439 | 440 | return NULL; 441 | } 442 | 443 | uint8_t EmvL2Repo::parseTag(uint8_t* buff, uint32_t* val) 444 | { 445 | uint8_t TAGLen = 1; 446 | uint32_t value = 0; 447 | int i = 0; 448 | 449 | value = buff[i]; 450 | if ((buff[i++] & 0x1F) == 0x1F) { 451 | TAGLen++; 452 | value = (value << 8) | buff[i]; 453 | while ((buff[i++] & 0x80) == 0x80) { 454 | TAGLen++; 455 | value = (value << 8) | buff[i]; 456 | } 457 | } 458 | 459 | *val = value; 460 | return TAGLen; 461 | } 462 | 463 | int EmvL2Repo::cardBrand() 464 | { 465 | int rv = 0; 466 | 467 | auto aid = getTag(0x84)->val.data(); 468 | if (memcmp(aid, "\xA0\x00\x00\x00\x03\x10\x10", 7) == 0) 469 | rv = BVISA; 470 | else if (memcmp(aid, "\xA0\x00\x00\x00\x04\x10\x10", 7) == 0) 471 | rv = BMASTER; 472 | else if (memcmp(aid, "\xA0\x00\x00\x00\x04\x30\x60", 7) == 0) 473 | rv = BEUROPAY; 474 | else if ((memcmp(aid, "\xA0\x00\x00\x00\x65\x10\x10", 7) == 0) || (memcmp(aid, "\xA0\x00\x00\x00\x89\x01\x23", 7) == 0)) 475 | rv = BJCB; 476 | 477 | return rv; 478 | } -------------------------------------------------------------------------------- /corelib/emvl2Repo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | class EmvL2Repo { 11 | private: 12 | EmvL2Repo(); 13 | 14 | public: 15 | static EmvL2Repo& getInstance(); 16 | ~EmvL2Repo(); 17 | emvl2Ret init(IDevice* device); 18 | 19 | 20 | int addCaKey(emvl2CAKey key); 21 | int addAidPrms(emvl2AIDPrms prms); 22 | 23 | void clearCaKeys(); 24 | void clearAidPrms(); 25 | void clearApdu(); 26 | int searchCAKeys(void); 27 | 28 | void clearCardTags(); 29 | 30 | int parseTags(uint8_t* buff, int buffLen); 31 | Tlv* setTag(uint32_t tag, uint8_t* src = NULL, uint8_t len = 0); 32 | Tlv* getTag(uint32_t tag); 33 | void setTagFlag(uint32_t tag, int flag); 34 | bool isTagFlag(uint32_t tag, int flag); 35 | bool isTagExist(uint32_t tag); 36 | uint32_t parseLen(uint8_t* data, uint8_t* lenLen = NULL); 37 | uint32_t parseTag(uint8_t* data, uint8_t* tagLen = NULL); 38 | int getTagFormat(uint32_t tag); 39 | void nextTag(uint8_t* data, int* idx); 40 | int cardBrand(); 41 | 42 | private: 43 | uint8_t* getTagDatas(uint32_t tag, int* i = NULL, int* j = NULL, int flag = 0); 44 | uint8_t parseTag(uint8_t* buff, uint32_t* val); 45 | 46 | public: 47 | vector aidPrms; 48 | vector script71; 49 | vector script72; 50 | emvl2Apdu apdu; 51 | vector transactionHashData; 52 | vector staticAppData; 53 | bool signatureRequired; 54 | bool pinBypassed; 55 | 56 | uint8_t iccPinPkModulus[PUBKEYMODULUSLEN]; 57 | uint8_t caPkModulus[PUBKEYMODULUSLEN]; 58 | uint8_t issPkModulus[PUBKEYMODULUSLEN]; 59 | uint8_t iccPkModulus[PUBKEYMODULUSLEN]; 60 | uint8_t recPkModulus[PUBKEYMODULUSLEN]; 61 | uint8_t recoveredData[PUBKEYMODULUSLEN]; 62 | 63 | emvl2CAKey* activeCAKey; 64 | uint8_t sdaTermData[256]; 65 | uint8_t typeOfAuth; 66 | uint8_t cdaIccPkModLen; 67 | uint8_t performImeediateSecondGenAc; 68 | uint8_t adviceReversal; 69 | uint8_t verifyDDAACFail; 70 | private: 71 | static EmvL2Repo* instance; 72 | IDevice* device; 73 | 74 | vector caKeys; 75 | map tags; 76 | }; 77 | -------------------------------------------------------------------------------- /corelib/emvl2SecUtil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "IDevice.h" 3 | #include "emvl2Repo.h" 4 | #include "emvl2Util.h" 5 | 6 | using namespace std; 7 | 8 | class EmvL2SecUtil { 9 | private: 10 | EmvL2SecUtil(); 11 | 12 | public: 13 | static EmvL2SecUtil& getInstance(); 14 | ~EmvL2SecUtil(); 15 | int init(IDevice* device); 16 | uint8_t determineDataAuthType(uint8_t* authType); 17 | 18 | int recoverPubKeyCert(emvl2PkMode pkType, uint8_t modLen,uint8_t* exponent, int exlen, uint8_t certLen,uint8_t* certData, uint8_t* issPkModLen); 19 | void rsaEncrypt(emvl2PkMode pkType, uint8_t modLen, uint8_t* exponent, uint8_t exlen, uint8_t* data, uint8_t dataLength); 20 | int recoverICCPINEncPubKeyCert(uint8_t modLen, uint8_t* iccPinModLen); 21 | int recoverICCPubKeyCert(uint8_t modLen, uint8_t* iccPinModLen); 22 | void prepStaticTagListData(int* lstlen); 23 | int verifyDynamicSign(emvl2PkMode pkType, uint8_t modLen,uint8_t* data, uint8_t dataLength); 24 | int genACCDATemplate80Processing(); 25 | int genACNOCDATemplate80Processing(); 26 | int genACCDATemplate77Processing(bool isCda); 27 | int genACNOCDATemplate77Processing(); 28 | int verifyDynamicSignAC(emvl2PkMode pkType, uint8_t modLen, uint8_t* cid); 29 | 30 | private: 31 | static EmvL2SecUtil* instance; 32 | IDevice* device; 33 | EmvL2Repo& repo; 34 | EmvL2Util& util; 35 | }; 36 | -------------------------------------------------------------------------------- /corelib/emvl2TagDef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Application Interchange Profile 4 | #define SDASupported 0x40 5 | #define DDASupported 0x20 6 | #define CVMSupported 0x10 7 | #define TRMNGPerformed 0x08 8 | #define IAUTHSupported 0x04 9 | #define CDASupported 0x01 10 | 11 | // Terminal Verification Results 12 | #define OFFAUTHNOTPERFORMED 0x80 //Offline data authentication was not performed 13 | #define SDAFAILED 0x40 //SDA failed 14 | #define ICCDATAMISSING 0x20 //ICC data missing 15 | #define CARDINHARDLIST 0x10 //Card number appears on hotlist 16 | #define DDAFAILED 0x08 //DDA failed 17 | #define CDAFAILED 0x04 //CDA failed 18 | #define SDASELECTED 0x02 //SDA was selected 19 | 20 | #define MISSMATCHAPPVERSIONS 0x180 //Card and terminal have different application versions 21 | #define EXPIREDAPP 0x140 //Expired application 22 | #define APPNOTYETEFFECTIVE 0x120 //Application not yet effective 23 | #define SERVICENOTALLOWED 0x110 //Requested service not allowed for card product 24 | #define NEWCARD 0x108 //New card 25 | 26 | #define CVMNOTSUCCESS 0x280 //Cardholder verification was not successful 27 | #define UNRECOGNISEDCVM 0x240 //Unrecognised CVM 28 | #define PINTRYLIMITEXCEEDED 0x220 //PIN try limit exceeded 29 | #define PINPADNOTPRESENT 0x210 //PIN entry required, but no PIN pad present or not working 30 | #define PINNOTENTERED 0x208 //PIN entry required, PIN pad present, but PIN was not entered 31 | #define ONLINEPINENTERED 0x204 //On-line PIN entered 32 | 33 | #define FLOORLIMITEXCEEDED 0x380 //Transaction exceeds floor limit 34 | #define LCONOFFLIMITEXCEEDED 0x340 //Lower consecutive offline limit exceeded 35 | #define UCONOFFLIMITEXCEEDED 0x320 //Upper consecutive offline limit exceeded 36 | #define TRANRANDSELECTEDONL 0x310 //Transaction selected randomly of on-line processing 37 | #define MERCHFORCEDONLINE 0x308 //Merchant forced transaction on-line 38 | 39 | #define DEFAULTTDOLUSED 0x480 //Default TDOL Used 40 | #define ISSUERAUTHFAILED 0x440 //Issuer authentication failed 41 | #define ISSUERSCRIPTFAILED1 0x420 //Script processing failed before final Generate AC 42 | #define ISSUERSCRIPTFAILED2 0x410 //Script processing failed after final Generate AC 43 | 44 | // Transaction Status Indicator 45 | #define OFFAUTHPERFORMED 0x80 //Offline data authentication was performed 46 | #define CVMPERFORMED 0x40 //Cardholder verification was performed 47 | #define CARDRISKMNGPERFORMED 0x20 //Card risk management was performed 48 | #define ISSUERAUTHPERFORMED 0x10 //Issuer authentication was performed 49 | #define TERMRISKMNGPERFORMED 0x08 //Terminal risk management was performed 50 | #define ISSUERSCRIPTPERFORMED 0x04 //Script processing was performed 51 | 52 | 53 | // Terminal Capabilities 54 | #define OFFLINEPLAINPIN 0x180 //Plaintext PIN for offline ICC verification 55 | #define ONLINEENCHIPEREDPIN 0x140 //Enciphered PIN for online verification 56 | #define SIGNATURESUPPORTED 0x120 //Signature (paper) 57 | #define OFFLINEENCHIPEREDPIN 0x110 //Enciphered PIN for offline verification 58 | #define NOCVMSUPPORTED 0x108 //No CVM Required 59 | #define SDASUPPORTED 0x280 //Static Data Authentication (SDA) 60 | #define DDASUPPORTED 0x240 //Dynamic Data Authentication (DDA) 61 | #define CDASUPPORTED 0x208 //Combined DDA/Application Cryptogram Generation 62 | 63 | // Application Usage Control 64 | #define DOMESTICCASHVALID 0x80 //Valid for domestic cash transactions 65 | #define INTERNATIONALCASHVALID 0x40 //Valid for international cash transactions 66 | #define DOMESTICGOODSVALID 0x20 //Valid for domestic goods 67 | #define INTERNATIONALGOODSVALID 0x10 //Valid for international goods 68 | #define DOMESTICSERVICESVALID 0x08 //Valid for domestic services 69 | #define INTERNATIONALSERVICESVALID 0x04 //Valid for international services 70 | #define ATMSVALID 0x02 //Valid at ATMs 71 | #define NONATMSVALID 0x01 //Valid at terminals other than ATMs 72 | #define DOMESTICCASHBACKALLOWED 0x180 //Domestic cashback is allowed 73 | #define INTERCASHBACKALLOWED 0x140 //International cashback is allowed 74 | 75 | // Additional Terminal Capabilities 76 | #define ACCASH 0x80 77 | #define ACGOODS 0x40 78 | #define ACSERVICES 0x20 79 | #define ACCASHBACK 0x10 80 | 81 | // Transaction Types 82 | #define TRNSALE 0x00 83 | #define TRNCASH 0x01 84 | #define TRNCASHBACK 0x09 85 | 86 | // Brands 87 | #define BVISA 3 88 | #define BEUROPAY 41 89 | #define BMASTER 43 90 | #define BJCB 6 91 | 92 | #define SERVICE_NOT_ALLOWED 0x01 93 | 94 | 95 | -------------------------------------------------------------------------------- /corelib/emvl2TermActionAnalysis.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2TermActionAnalysis.h" 2 | 3 | EmvL2TermActionAnalysis::EmvL2TermActionAnalysis(IDevice& device) : device(device), repo(EmvL2Repo::getInstance()), 4 | command(EmvL2Command::getInstance()), util(EmvL2Util::getInstance()) { 5 | } 6 | 7 | EmvL2TermActionAnalysis::~EmvL2TermActionAnalysis() { 8 | 9 | } 10 | 11 | uint8_t EmvL2TermActionAnalysis::perform(uint8_t* termDecision) { 12 | uint8_t* tvr = repo.getTag(0x95)->val.data(); 13 | 14 | Tlv* tag8C = repo.getTag(0x8C); 15 | tcHash(tag8C->val.data(), tag8C->len); 16 | 17 | if (repo.isTagFlag(0x95, MERCHFORCEDONLINE)) 18 | { 19 | *termDecision = ARQC; 20 | return success; 21 | } 22 | 23 | uint8_t iacDenial[5] = { 0 }; 24 | if (repo.isTagExist(0x9F0E)) 25 | memcpy(iacDenial, repo.getTag(0x9F0E)->val.data(), 5); 26 | 27 | uint8_t tacDenial[5] = {0}; 28 | if (repo.isTagExist(0xDF8121)) 29 | memcpy(tacDenial, repo.getTag(0xDF8121)->val.data(), sizeof(tacDenial)); 30 | 31 | for (int i = 0; i < 5; i++) 32 | { 33 | uint8_t cBit = 0x80; 34 | 35 | do 36 | { 37 | if ((tvr[i] & cBit) == cBit) 38 | { 39 | if ((tacDenial[i] & cBit) || (iacDenial[i] & cBit)) 40 | { 41 | repo.setTag(0x8A, (uint8_t*)"Z1", 2); 42 | *termDecision = AAC; 43 | return success; 44 | } 45 | } 46 | cBit >>= 1; 47 | } while (cBit != 0); 48 | } 49 | 50 | uint8_t ttype = repo.getTag(0x9F35)->val.data()[0]; 51 | if (ttype == 0x11 || ttype == 0x14 || ttype == 0x21 || ttype == 0x24) 52 | { 53 | *termDecision = ARQC; 54 | return success; 55 | } 56 | 57 | uint8_t iacOnline[5] = { 0 }; 58 | if (repo.isTagExist(0x9F0F)) 59 | memcpy(iacOnline, repo.getTag(0x9F0F)->val.data(), sizeof(iacOnline)); 60 | 61 | uint8_t tacOnline[5] = { 0 }; 62 | if (!repo.isTagExist(0xDF8122)) 63 | memcpy(tacOnline, repo.getTag(0xDF8122)->val.data(), sizeof(tacOnline)); 64 | 65 | for (int i = 0; i < 5; i++) 66 | { 67 | uint8_t cBit = 0x80; 68 | 69 | do 70 | { 71 | if (tvr[i] & cBit) 72 | { 73 | if ((tacOnline[i] & cBit) || (iacOnline[i] & cBit)) 74 | { 75 | *termDecision = ARQC; 76 | return success; 77 | } 78 | } 79 | cBit >>= 1; 80 | } while (cBit != 0); 81 | } 82 | 83 | repo.setTag(0x8A, (uint8_t *)"Y1", 2); 84 | *termDecision = TC; 85 | return success; 86 | } 87 | 88 | void EmvL2TermActionAnalysis::tcHash(uint8_t* data, uint8_t len) 89 | { 90 | bool defaultTdol = false; 91 | 92 | Tlv* tag97 = repo.getTag(0x97); 93 | Tlv* tagDF8B13 = repo.getTag(0xDF8B13); 94 | 95 | int tdolLen = 0; 96 | if (tag97) { 97 | tdolLen = tag97->len; 98 | } 99 | else if(tagDF8B13) { 100 | tdolLen = tagDF8B13->len; 101 | defaultTdol = true; 102 | } 103 | 104 | int idx = 0; 105 | while (idx < len) 106 | { 107 | if (data[idx] == 0x9F || data[idx] == 0x5F || 108 | data[idx] == 0xDF || data[idx] == 0xBF) 109 | { 110 | idx += 3; 111 | } 112 | else 113 | { 114 | if (data[idx] == 0x98 && tdolLen != 0 && defaultTdol) 115 | { 116 | repo.isTagFlag(0x95, DEFAULTTDOLUSED); 117 | break; 118 | } 119 | 120 | idx += 2; 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /corelib/emvl2TermActionAnalysis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Command.h" 6 | #include "emvl2Util.h" 7 | 8 | using namespace std; 9 | 10 | class EmvL2TermActionAnalysis { 11 | public: 12 | EmvL2TermActionAnalysis(IDevice& device); 13 | ~EmvL2TermActionAnalysis(); 14 | uint8_t perform(uint8_t* termDecision); 15 | 16 | private: 17 | void tcHash(uint8_t* data, uint8_t len); 18 | 19 | private: 20 | IDevice& device; 21 | EmvL2Repo& repo; 22 | EmvL2Command& command; 23 | EmvL2Util& util; 24 | }; 25 | -------------------------------------------------------------------------------- /corelib/emvl2TerminalRiskMng.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2TerminalRiskMng.h" 2 | 3 | EmvL2TerminalRiskMng::EmvL2TerminalRiskMng(IDevice& device) : device(device), repo(EmvL2Repo::getInstance()), 4 | command(EmvL2Command::getInstance()), util(EmvL2Util::getInstance()) { 5 | } 6 | 7 | EmvL2TerminalRiskMng::~EmvL2TerminalRiskMng() { 8 | 9 | } 10 | 11 | uint8_t EmvL2TerminalRiskMng::perform() { 12 | int rv; 13 | 14 | repo.staticAppData.clear(); 15 | 16 | rv = randomTranSelect(); 17 | if (rv != success) 18 | { 19 | return rv; 20 | } 21 | 22 | rv = velocityCheck(); 23 | if (rv != success) 24 | { 25 | return rv; 26 | } 27 | 28 | repo.setTagFlag(0x9B, TERMRISKMNGPERFORMED); 29 | return success; 30 | } 31 | 32 | int EmvL2TerminalRiskMng::velocityCheck() 33 | { 34 | int atc = 0, lastOnlineAtc = 0, rv; 35 | bool lastOnlineAtcZero = false, atcReadError = false, lastOnlineAtcReadError = false; 36 | uint8_t sw1, sw2; 37 | 38 | if (!repo.isTagExist(0x9F23) || !repo.isTagExist(0x9F14)) 39 | { 40 | return success;; 41 | } 42 | 43 | rv = command.getData(0x9F36); 44 | if (rv != success) 45 | { 46 | return rv; 47 | } 48 | 49 | sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 50 | sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 51 | 52 | if ((sw1 == 0x90) && (sw2 == 0)) 53 | { 54 | rv = repo.parseTags(&repo.apdu.rdata[0], repo.apdu.rlen - 2); 55 | 56 | if (rv == success) 57 | { 58 | atc = util.bin2Int(repo.getTag(0x9F36)->val.data(), repo.getTag(0x9F36)->len); 59 | } 60 | } 61 | 62 | if ((sw1 == 0x6A) && ((sw2 == 0x81) || (sw2 == 0x88))) 63 | { 64 | repo.setTagFlag(0x95, ICCDATAMISSING); 65 | repo.setTagFlag(0x95, LCONOFFLIMITEXCEEDED); 66 | repo.setTagFlag(0x95, UCONOFFLIMITEXCEEDED); 67 | atcReadError = true; 68 | } 69 | else if ((sw1 != 0x90) || (sw2 != 0)) 70 | { 71 | repo.setTagFlag(0x95, ICCDATAMISSING); 72 | repo.setTagFlag(0x95, LCONOFFLIMITEXCEEDED); 73 | repo.setTagFlag(0x95, UCONOFFLIMITEXCEEDED); 74 | return success; 75 | } 76 | 77 | if (!repo.isTagExist(0x9F36)) 78 | { 79 | repo.setTagFlag(0x95, ICCDATAMISSING); 80 | repo.setTagFlag(0x95, LCONOFFLIMITEXCEEDED); 81 | repo.setTagFlag(0x95, UCONOFFLIMITEXCEEDED); 82 | atcReadError = true; 83 | } 84 | 85 | rv = command.getData(0x9F13); 86 | if (rv != success) 87 | { 88 | return rv; 89 | } 90 | 91 | sw1 = repo.apdu.rdata[repo.apdu.rlen - 2]; 92 | sw2 = repo.apdu.rdata[repo.apdu.rlen - 1]; 93 | if ((sw1 == 0x90) && (sw2 == 0)) 94 | { 95 | rv = repo.parseTags(&repo.apdu.rdata[0], repo.apdu.rlen - 2); 96 | if (rv == success) 97 | { 98 | Tlv* tag9F13 = repo.getTag(0x9F13); 99 | lastOnlineAtc = util.bin2Int(tag9F13->val.data(), tag9F13->len); 100 | 101 | if (tag9F13->val.data()[0] == 0 && tag9F13->val.data()[1] == 0) 102 | { 103 | lastOnlineAtcZero = true; 104 | } 105 | } 106 | } 107 | 108 | if ((sw1 == 0x6A) && ((sw2 == 0x81) || (sw2 == 0x88))) 109 | { 110 | repo.setTagFlag(0x95, ICCDATAMISSING); 111 | repo.setTagFlag(0x95, LCONOFFLIMITEXCEEDED); 112 | repo.setTagFlag(0x95, UCONOFFLIMITEXCEEDED); 113 | lastOnlineAtcReadError = true; 114 | } 115 | else if ((sw1 != 0x90) || (sw2 != 0) || !repo.isTagExist(0x9F13)) 116 | { 117 | repo.setTagFlag(0x95, ICCDATAMISSING); 118 | repo.setTagFlag(0x95, LCONOFFLIMITEXCEEDED); 119 | repo.setTagFlag(0x95, UCONOFFLIMITEXCEEDED); 120 | return success; 121 | } 122 | 123 | if (lastOnlineAtc == 0 && !repo.isTagFlag(0x95, LCONOFFLIMITEXCEEDED) && !repo.isTagFlag(0x95, UCONOFFLIMITEXCEEDED)) 124 | { 125 | repo.setTagFlag(0x95, NEWCARD); 126 | } 127 | 128 | if (atc == 0 && lastOnlineAtcZero && repo.isTagFlag(0x95, LCONOFFLIMITEXCEEDED) && repo.isTagFlag(0x95, UCONOFFLIMITEXCEEDED)) 129 | { 130 | repo.setTagFlag(0x95, NEWCARD); 131 | } 132 | 133 | if (atcReadError && lastOnlineAtcReadError) 134 | { 135 | return success; 136 | } 137 | 138 | if (atc <= lastOnlineAtc) 139 | { 140 | repo.setTagFlag(0x95, LCONOFFLIMITEXCEEDED); 141 | repo.setTagFlag(0x95, UCONOFFLIMITEXCEEDED); 142 | return success; 143 | } 144 | 145 | uint8_t lcOffLimit = repo.getTag(0x9F14)->val.data()[0]; 146 | if ((atc - lastOnlineAtc) > lcOffLimit) 147 | { 148 | repo.setTagFlag(0x95, LCONOFFLIMITEXCEEDED); 149 | } 150 | 151 | uint8_t ucOffLimit = repo.getTag(0x9F23)->val.data()[0]; 152 | if ((atc - lastOnlineAtc) > ucOffLimit) 153 | { 154 | repo.setTagFlag(0x95, UCONOFFLIMITEXCEEDED); 155 | } 156 | 157 | return success; 158 | } 159 | 160 | int EmvL2TerminalRiskMng::randomTranSelect() 161 | { 162 | uint32_t trnAmount = 0, floorLimit = 0, thrsValue, trnTargetPerc, mTargetPerc, targetPerc, randNo, factor; 163 | uint8_t rnd; 164 | 165 | Tlv* tag9F1B = repo.getTag(0x9F1B); 166 | if (tag9F1B) 167 | { 168 | trnAmount = util.bcd2Int(repo.getTag(0x9F02)->val.data(), repo.getTag(0x9F02)->len); 169 | 170 | if (!repo.isTagExist(0x9F03)) 171 | { 172 | util.bcd2Int(repo.getTag(0x9F03)->val.data(), repo.getTag(0x9F03)->len); 173 | } 174 | 175 | floorLimit = util.bin2Int(tag9F1B->val.data(), tag9F1B->len); 176 | if (trnAmount >= floorLimit) 177 | { 178 | repo.setTagFlag(0x95, FLOORLIMITEXCEEDED); 179 | return success; 180 | } 181 | } 182 | 183 | if (!repo.isTagExist(0xDF8B11) || !repo.isTagExist(0xDF8B15) || !repo.isTagExist(0xDF8B14)) 184 | { 185 | return success; 186 | } 187 | 188 | thrsValue = util.bin2Int(repo.getTag(0xDF8B11)->val.data(), repo.getTag(0xDF8B11)->len); 189 | targetPerc = util.bin2Int(repo.getTag(0xDF8B15)->val.data(), repo.getTag(0xDF8B15)->len); 190 | mTargetPerc = util.bin2Int(repo.getTag(0xDF8B14)->val.data(), repo.getTag(0xDF8B14)->len); 191 | 192 | if (success != device.genRand(&rnd, sizeof(rnd))) 193 | { 194 | return failure; 195 | } 196 | randNo = (rnd % 100) * 100; 197 | 198 | if (trnAmount < thrsValue) 199 | { 200 | if ((targetPerc * 100) >= randNo) 201 | { 202 | repo.setTagFlag(0x95, TRANRANDSELECTEDONL); 203 | return success; 204 | } 205 | } 206 | else if (trnAmount >= floorLimit) 207 | { 208 | repo.setTagFlag(0x95, FLOORLIMITEXCEEDED); 209 | } 210 | 211 | else 212 | { 213 | factor = ((100 * (trnAmount - thrsValue)) / (floorLimit - thrsValue)); 214 | trnTargetPerc = factor * (mTargetPerc - targetPerc) + targetPerc * 100; 215 | 216 | if (trnTargetPerc >= randNo) 217 | { 218 | repo.setTagFlag(0x95, TRANRANDSELECTEDONL); 219 | return success; 220 | } 221 | } 222 | 223 | return success; 224 | } 225 | 226 | -------------------------------------------------------------------------------- /corelib/emvl2TerminalRiskMng.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "emvl2Defs.h" 3 | #include "IDevice.h" 4 | #include "emvl2Repo.h" 5 | #include "emvl2Command.h" 6 | #include "emvl2Util.h" 7 | 8 | using namespace std; 9 | 10 | class EmvL2TerminalRiskMng { 11 | public: 12 | EmvL2TerminalRiskMng(IDevice& device); 13 | ~EmvL2TerminalRiskMng(); 14 | uint8_t perform(); 15 | 16 | private: 17 | int randomTranSelect(); 18 | int velocityCheck(); 19 | 20 | private: 21 | IDevice& device; 22 | EmvL2Repo& repo; 23 | EmvL2Command& command; 24 | EmvL2Util& util; 25 | }; 26 | -------------------------------------------------------------------------------- /corelib/emvl2Util.cpp: -------------------------------------------------------------------------------- 1 | #include "emvl2Util.h" 2 | #include "emvl2Defs.h" 3 | #include "emvl2Repo.h" 4 | 5 | #define ISODD(p) ((p%2)==1) 6 | 7 | EmvL2Util* EmvL2Util::instance = NULL; 8 | 9 | EmvL2Util& EmvL2Util::getInstance() { 10 | if (!instance) { 11 | instance = new EmvL2Util(); 12 | } 13 | return *instance; 14 | } 15 | 16 | EmvL2Util::EmvL2Util() : repo(EmvL2Repo::getInstance()) { 17 | device = NULL; 18 | } 19 | 20 | EmvL2Util::~EmvL2Util() { 21 | 22 | } 23 | 24 | emvl2Ret EmvL2Util::init(IDevice* device) { 25 | this->device = device; 26 | 27 | return success; 28 | } 29 | 30 | uint32_t EmvL2Util::bin2Int(uint8_t* in, uint8_t inLength) 31 | { 32 | uint32_t ret; 33 | uint8_t i, j, buf[sizeof(uint32_t)]; 34 | 35 | if (inLength > sizeof(uint32_t)) 36 | { 37 | ret = 0; 38 | } 39 | else 40 | { 41 | for (i = 0; i < sizeof(uint32_t) - inLength; i++) 42 | { 43 | buf[i] = 0; 44 | } 45 | for (j = i; j < sizeof(uint32_t); j++) 46 | { 47 | buf[j] = in[j - i]; 48 | } 49 | ret = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]); 50 | } 51 | 52 | return ret; 53 | } 54 | 55 | uint32_t EmvL2Util::bcd2Int(uint8_t* in, uint8_t inLength) 56 | { 57 | uint32_t rv = 0; 58 | 59 | while (inLength--) 60 | { 61 | rv = (rv * 100) + (((*in >> 4) & 0x0F) * 10) + (*in & 15); 62 | in++; 63 | } 64 | 65 | return rv; 66 | } 67 | 68 | void EmvL2Util::bcd2Str(uint8_t* in, uint16_t inLength, uint8_t* out) 69 | { 70 | const uint8_t h[] = "0123456789ABCDEF"; 71 | uint16_t i; 72 | 73 | for (i = 0; i < inLength; i++) 74 | { 75 | *out++ = h[(in[i] >> 4) & 0x0F]; 76 | *out++ = h[in[i] & 0x0F]; 77 | } 78 | *out = 0; 79 | } 80 | 81 | uint8_t EmvL2Util::adjustYear(uint16_t year) 82 | { 83 | uint8_t rv; 84 | 85 | if (year > 2000) 86 | { 87 | rv = (uint8_t)(year - 2000); 88 | } 89 | else if (year > 1900) 90 | { 91 | rv = (uint8_t)(year - 1900); 92 | } 93 | else if (year < 100) 94 | { 95 | rv = (uint8_t)year; 96 | } 97 | else 98 | { 99 | rv = (uint8_t)year; 100 | } 101 | 102 | return rv; 103 | } 104 | 105 | uint8_t EmvL2Util::byte2Bcd(uint8_t b) 106 | { 107 | return ((b / 10) << 4) | (b % 10); 108 | } 109 | 110 | void EmvL2Util::collectDolData(uint8_t* dol, int dolLen, uint8_t* dolData, uint8_t* dolDataLen) 111 | { 112 | int index = 0; 113 | uint8_t actualLength = 0, lenInDOL = 0, tagLen = 0; 114 | uint8_t tcHash[20] = { 0 }, lenLen = 0; 115 | uint32_t valueOfTag = 0; 116 | int16_t indexOfTag = 0; 117 | *dolDataLen = 0; 118 | 119 | while (index < dolLen) 120 | { 121 | valueOfTag = repo.parseTag(&dol[index], &tagLen); 122 | index += tagLen; 123 | 124 | Tlv* tag = repo.getTag(valueOfTag); 125 | 126 | if (tag) 127 | { 128 | lenInDOL = dol[index++]; 129 | 130 | if (valueOfTag == 0x98) 131 | { 132 | calculateTCHash(tcHash); 133 | 134 | if (lenInDOL == 20) 135 | { 136 | memcpy(&dolData[*dolDataLen], tcHash, lenInDOL); 137 | *dolDataLen += lenInDOL; 138 | continue; 139 | } 140 | else if (lenInDOL > 20) 141 | { 142 | memset(&dolData[*dolDataLen], 0, lenInDOL - 20); 143 | *dolDataLen += lenInDOL - 20; 144 | memcpy(&dolData[*dolDataLen], tag->val.data(), 20); 145 | *dolDataLen += 20; 146 | continue; 147 | } 148 | } 149 | 150 | actualLength = tag->len; 151 | if (actualLength == 0) 152 | { 153 | memset(&dolData[*dolDataLen], 0, lenInDOL); 154 | *dolDataLen += lenInDOL; 155 | } 156 | else 157 | { 158 | if (lenInDOL < actualLength) 159 | { 160 | if (repo.cardBrand() == BVISA) 161 | { 162 | if ((repo.getTagFormat(valueOfTag) == FNUM)) 163 | { 164 | int align = tag->len - lenInDOL; 165 | memcpy(&dolData[*dolDataLen], &tag->val.data()[align], lenInDOL); 166 | *dolDataLen += lenInDOL; 167 | } 168 | else 169 | { 170 | memcpy(&dolData[*dolDataLen], tag->val.data(), lenInDOL); 171 | *dolDataLen += lenInDOL; 172 | } 173 | } 174 | else if ((repo.cardBrand() == BEUROPAY) || (repo.cardBrand() == BMASTER)) 175 | { 176 | if (repo.getTagFormat(valueOfTag) == FNUM) 177 | { 178 | int align = tag->len - lenInDOL; 179 | memcpy(&dolData[*dolDataLen], &tag->val.data()[align], lenInDOL); 180 | *dolDataLen += lenInDOL; 181 | } 182 | else 183 | { 184 | memcpy(&dolData[*dolDataLen], tag->val.data(), lenInDOL); 185 | *dolDataLen += lenInDOL; 186 | } 187 | } 188 | else 189 | { 190 | if (repo.getTagFormat(valueOfTag) == FNUM) 191 | { 192 | int align = tag->len - lenInDOL; 193 | memcpy(&dolData[*dolDataLen], &tag->val.data()[align], lenInDOL); 194 | *dolDataLen += lenInDOL; 195 | } 196 | else 197 | { 198 | memcpy(&dolData[*dolDataLen], tag->val.data(), lenInDOL); 199 | *dolDataLen += lenInDOL; 200 | } 201 | } 202 | } 203 | else if (lenInDOL > actualLength) 204 | { 205 | if (repo.getTagFormat(valueOfTag) == FNUM) 206 | { 207 | memset(&dolData[*dolDataLen], 0, lenInDOL - tag->len); 208 | *dolDataLen += lenInDOL - tag->len; 209 | memcpy(&dolData[*dolDataLen], tag->val.data(), actualLength); 210 | *dolDataLen += actualLength; 211 | } 212 | else if (repo.getTagFormat(valueOfTag) == FCNM) 213 | { 214 | memcpy(&dolData[*dolDataLen], tag->val.data(), actualLength); 215 | *dolDataLen += actualLength; 216 | memset(&dolData[*dolDataLen], 0xFF, lenInDOL - actualLength); 217 | *dolDataLen += lenInDOL - tag->len; 218 | } 219 | else 220 | { 221 | memcpy(&dolData[*dolDataLen], tag->val.data(), actualLength); 222 | *dolDataLen += tag->len; 223 | memset(&dolData[*dolDataLen], 0, lenInDOL - actualLength); 224 | *dolDataLen += lenInDOL - actualLength; 225 | } 226 | } 227 | else 228 | { 229 | memcpy(&dolData[*dolDataLen], tag->val.data(), lenInDOL); 230 | *dolDataLen += lenInDOL; 231 | } 232 | } 233 | continue; 234 | } 235 | else 236 | { 237 | if (index < dolLen) 238 | { 239 | lenInDOL = repo.parseLen(&dol[index], &lenLen); 240 | index += lenLen; 241 | memset(&dolData[*dolDataLen], 0, lenInDOL); 242 | *dolDataLen += lenInDOL; 243 | } 244 | else 245 | { 246 | dolData[*dolDataLen] = 0; 247 | *dolDataLen += 1; 248 | } 249 | } 250 | } 251 | } 252 | 253 | void EmvL2Util::calculateTCHash(uint8_t* tcHash) 254 | { 255 | uint8_t tdolData[DOLBUFFERLEN]; 256 | uint8_t* tdol; 257 | uint8_t tdolDataLen = 0; 258 | int tdolLen = 0; 259 | 260 | Tlv* tag97 = repo.getTag(0x97); 261 | if (!tag97) 262 | { 263 | Tlv* tagDF8B13 = repo.getTag(0xDF8B13); 264 | 265 | tdolLen = tagDF8B13->len; 266 | tdol = tagDF8B13->val.data(); 267 | 268 | if (tdolLen != 0) 269 | { 270 | repo.setTagFlag(0x95, DEFAULTTDOLUSED); 271 | } 272 | } 273 | else { 274 | tdolLen = tag97->len; 275 | tdol = tag97->val.data(); 276 | } 277 | 278 | collectTdolData(tdol,tdolLen, tdolData, &tdolDataLen); 279 | device->sha1(tdolData, tdolDataLen, tcHash); 280 | } 281 | 282 | void EmvL2Util::collectTdolData(uint8_t* dol, int dolLen, uint8_t* dolData, uint8_t* dolDataLen) 283 | { 284 | int index = 0; 285 | uint8_t actualLength, lengthInDOL, tagLen; 286 | uint32_t valueOfTag = 0; 287 | int16_t indexOfTag = 0; 288 | *dolDataLen = 0; 289 | 290 | while (index < dolLen) 291 | { 292 | valueOfTag = repo.parseTag(&dol[index], &tagLen); 293 | index += tagLen; 294 | 295 | Tlv* tag = repo.getTag(valueOfTag); 296 | 297 | if (tag) 298 | { 299 | lengthInDOL = dol[index++]; 300 | 301 | actualLength = tag->len; 302 | if (actualLength == 0) 303 | { 304 | memset(&dolData[*dolDataLen], 0, lengthInDOL); 305 | *dolDataLen += lengthInDOL; 306 | } 307 | else 308 | { 309 | if (lengthInDOL < actualLength) 310 | { 311 | if (repo.getTagFormat(valueOfTag) == FNUM) 312 | { 313 | int align = tag->len - lengthInDOL; 314 | memcpy(&dolData[*dolDataLen], &tag->val.data()[align], lengthInDOL); 315 | *dolDataLen += lengthInDOL; 316 | } 317 | else 318 | { 319 | memcpy(&dolData[*dolDataLen], tag->val.data(), lengthInDOL); 320 | *dolDataLen += lengthInDOL; 321 | } 322 | } 323 | else if (lengthInDOL > actualLength) 324 | { 325 | if ((repo.getTagFormat(valueOfTag) == FNUM)) 326 | { 327 | memset(&dolData[*dolDataLen], 0, lengthInDOL - tag->len); 328 | *dolDataLen += (lengthInDOL - actualLength); 329 | memcpy(&dolData[*dolDataLen], tag->val.data(), actualLength); 330 | *dolDataLen += actualLength; 331 | } 332 | else if (repo.getTagFormat(valueOfTag) == FCNM) 333 | { 334 | memcpy(&dolData[*dolDataLen], tag->val.data(), actualLength); 335 | *dolDataLen += actualLength; 336 | memset(&dolData[*dolDataLen], 0xFF, lengthInDOL - actualLength); 337 | *dolDataLen += (lengthInDOL - actualLength); 338 | } 339 | else 340 | { 341 | memcpy(&dolData[*dolDataLen], tag->val.data(), actualLength); 342 | *dolDataLen += actualLength; 343 | memset(&dolData[*dolDataLen], 0, lengthInDOL - actualLength); 344 | *dolDataLen += lengthInDOL - actualLength; 345 | } 346 | } 347 | else 348 | { 349 | memcpy(&dolData[*dolDataLen], tag->val.data(), lengthInDOL); 350 | *dolDataLen += lengthInDOL; 351 | } 352 | } 353 | continue; 354 | } 355 | else 356 | { 357 | lengthInDOL = dol[index++]; 358 | memset(&dolData[*dolDataLen], 0, lengthInDOL); 359 | *dolDataLen += lengthInDOL; 360 | } 361 | } 362 | } 363 | 364 | uint8_t* EmvL2Util::str2Bcd(uint8_t* str, uint16_t str_len, uint8_t* bcd, uint16_t bcdlen) 365 | { 366 | uint16_t i, index; 367 | uint8_t lnibble = 0; 368 | uint8_t unibble = 0; 369 | uint8_t* str_buf; 370 | uint16_t str_buf_len; 371 | 372 | str_buf = (uint8_t*)malloc(str_len + str_len % 2); 373 | if (NULL == str_buf) 374 | { 375 | return NULL; 376 | } 377 | 378 | if (ISODD(str_len)) 379 | { 380 | str_buf[0] = '0'; 381 | memcpy(str_buf + 1, str, str_len); 382 | str_buf_len = str_len + 1; 383 | } 384 | else 385 | { 386 | memcpy(str_buf, str, str_len); 387 | str_buf_len = str_len; 388 | } 389 | 390 | memset(bcd, 0, bcdlen); 391 | index = bcdlen - (str_len + 1) / 2; 392 | 393 | for (i = 0; i < str_buf_len; i++) 394 | { 395 | if (!ISODD(i)) 396 | { 397 | if ((str_buf[i] >= '0') && (str_buf[i] <= '9')) 398 | { 399 | unibble = str_buf[i] - '0'; 400 | } 401 | else if ((str_buf[i] >= 'A') && (str_buf[i] <= 'F')) 402 | { 403 | unibble = str_buf[i] - 0x37; 404 | } 405 | else 406 | { 407 | free(str_buf); 408 | return NULL; 409 | } 410 | 411 | bcd[index + i / 2] = unibble; 412 | } 413 | else 414 | { 415 | if ((str_buf[i] >= '0') && (str_buf[i] <= '9')) 416 | { 417 | lnibble = str_buf[i] - '0'; 418 | } 419 | else if ((str_buf[i] >= 'A') && (str_buf[i] <= 'F')) 420 | { 421 | lnibble = str_buf[i] - 0x37; 422 | } 423 | else 424 | { 425 | free(str_buf); 426 | return NULL; 427 | } 428 | 429 | bcd[index + i / 2] = (unibble << 4) | (lnibble); 430 | } 431 | } 432 | 433 | free(str_buf); 434 | return bcd; 435 | } 436 | 437 | uint8_t* EmvL2Util::hexStr2ByteArray(const char* hexStr) 438 | { 439 | int len = strlen(hexStr); 440 | 441 | uint8_t* buff = (uint8_t*)malloc(len / 2); 442 | if (buff == NULL) 443 | return NULL; 444 | int j = 0; 445 | 446 | for (int i = 0; i < len; i++) 447 | { 448 | if (i % 2 == 0) 449 | { 450 | int valueHigh = (int)(*(hexStr + i)); 451 | int valueLow = (int)(*(hexStr + i + 1)); 452 | 453 | valueHigh = hexAsc2Dec(valueHigh); 454 | valueLow = hexAsc2Dec(valueLow); 455 | 456 | valueHigh *= 16; 457 | int total = valueHigh + valueLow; 458 | *(buff + j++) = (uint8_t)total; 459 | } 460 | } 461 | return buff; 462 | } 463 | 464 | int EmvL2Util::hexAsc2Dec(int value) 465 | { 466 | if (value > 47 && value < 59) 467 | { 468 | value -= 48; 469 | } 470 | else if (value > 96 && value < 103) 471 | { 472 | value -= 97; 473 | value += 10; 474 | } 475 | else if (value > 64 && value < 71) 476 | { 477 | value -= 65; 478 | value += 10; 479 | } 480 | else 481 | { 482 | value = 0; 483 | } 484 | return value; 485 | } 486 | 487 | -------------------------------------------------------------------------------- /corelib/emvl2Util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "IDevice.h" 3 | #include "emvl2Repo.h" 4 | #include 5 | 6 | using namespace std; 7 | 8 | class EmvL2Util { 9 | private: 10 | EmvL2Util(); 11 | 12 | public: 13 | static EmvL2Util& getInstance(); 14 | ~EmvL2Util(); 15 | emvl2Ret init(IDevice* device); 16 | 17 | uint32_t bin2Int(uint8_t* in, uint8_t inLength); 18 | uint32_t bcd2Int(uint8_t* in, uint8_t inLength); 19 | void bcd2Str(uint8_t* in, uint16_t inLength, uint8_t* out); 20 | uint8_t byte2Bcd(uint8_t b); 21 | uint8_t* str2Bcd(uint8_t* str, uint16_t str_len, uint8_t* bcd, uint16_t bcdlen); 22 | uint8_t* hexStr2ByteArray(const char* hexStr); 23 | int hexAsc2Dec(int value); 24 | uint8_t adjustYear(uint16_t year); 25 | void collectDolData(uint8_t* dol, int dolLen, uint8_t* dolData, uint8_t* dolDataLen); 26 | private: 27 | void calculateTCHash(uint8_t* tcHash); 28 | void collectTdolData(uint8_t* dol, int dolLen, uint8_t* dolData, uint8_t* dolDataLen); 29 | 30 | private: 31 | static EmvL2Util* instance; 32 | IDevice* device; 33 | EmvL2Repo& repo; 34 | }; 35 | -------------------------------------------------------------------------------- /corelib/entrypoint.cpp: -------------------------------------------------------------------------------- 1 | #include "entrypoint.h" 2 | #include "emvl2Impl.h" 3 | #include "l1/deviceImpl.h" 4 | #include 5 | #include "emvl2Defs.h" 6 | 7 | using namespace std; 8 | 9 | EMVL2EXPORT int emvl2Version() { 10 | return 2; 11 | } 12 | 13 | EMVL2EXPORT int emvl2Init(const char* readerName, LogCallbackFunction logCb, PinCallbackFunction pinCb) { 14 | DeviceImpl* device = new DeviceImpl(readerName, logCb, pinCb); 15 | EmvL2Impl::getInstance().init(device); 16 | EmvL2Impl::getInstance().getDevice().logf("Init Done\n"); 17 | return 0; 18 | } 19 | 20 | EMVL2EXPORT int emvl2AddCaKey(const char* rid, uint8_t keyId, const char* modules, const char* exponent) { 21 | EmvL2Impl& emvl2Impl = EmvL2Impl::getInstance(); 22 | 23 | int modulesLen = strlen(modules) / 2; 24 | int exponentLen = strlen(exponent) / 2; 25 | int ridLen = strlen(exponent) / 2; 26 | 27 | uint8_t* ridBuff = EmvL2Util::getInstance().hexStr2ByteArray(rid); 28 | uint8_t* modulesBuff = EmvL2Util::getInstance().hexStr2ByteArray(modules); 29 | uint8_t* exponentBuff = EmvL2Util::getInstance().hexStr2ByteArray(exponent); 30 | 31 | int rv = emvl2Impl.addCAKey(ridBuff, keyId, modulesLen, modulesBuff, exponentBuff, exponentLen); 32 | 33 | free(ridBuff); 34 | free(modulesBuff); 35 | free(exponentBuff); 36 | 37 | return rv; 38 | } 39 | 40 | EMVL2EXPORT int emvl2AddAidPrms(const char* aid, const char* data) { 41 | EmvL2Impl& emvl2Impl = EmvL2Impl::getInstance(); 42 | 43 | emvl2Impl.getDevice().logf("%s:%s\n", aid, data); 44 | 45 | emvl2AIDPrms aidPrms = {0}; 46 | aidPrms.aidLen = (uint8_t)(strlen(aid) / 2); 47 | aidPrms.aid = EmvL2Util::getInstance().hexStr2ByteArray(aid); 48 | aidPrms.len = strlen(data) / 2; 49 | if (aidPrms.len > 0) 50 | aidPrms.data = EmvL2Util::getInstance().hexStr2ByteArray(data); 51 | 52 | int rv = emvl2Impl.addAidPrms(aidPrms); 53 | return rv; 54 | } 55 | 56 | EMVL2EXPORT int emvl2Start(uint8_t tranType, uint8_t accountType, const char* amount, const char* otherAmount) { 57 | EmvL2Impl& emvl2Impl = EmvL2Impl::getInstance(); 58 | 59 | emvl2Impl.getDevice().logf("Starting Transaction\n"); 60 | 61 | uint8_t termDecision = 0; 62 | uint8_t cardDecision = 0; 63 | 64 | uint8_t rv = failure; 65 | rv = emvl2Impl.getDevice().cardReset(); 66 | emvl2Impl.getDevice().logf("cardReset:%d\n", rv); 67 | if(rv == emvl2Ret::success) 68 | { 69 | rv = emvl2Impl.applicationSelection(); 70 | emvl2Impl.getDevice().logf("applicationSelection:%d\n", rv); 71 | if (rv == emvl2Ret::success) { 72 | rv = emvl2Impl.gpo(tranType, accountType, amount, otherAmount); 73 | emvl2Impl.getDevice().logf("gpo:%d\n", rv); 74 | if (rv == emvl2Ret::success) { 75 | rv = emvl2Impl.readAppData(); 76 | emvl2Impl.getDevice().logf("readAppData:%d\n", rv); 77 | if (rv == emvl2Ret::success) { 78 | rv = emvl2Impl.offlineDataAuth(); 79 | emvl2Impl.getDevice().logf("offlineDataAuth:%d\n", rv); 80 | if (rv == success) { 81 | rv = emvl2Impl.processRestrict(); 82 | emvl2Impl.getDevice().logf("processRestrict:%d\n", rv); 83 | if (rv == emvl2Ret::success) { 84 | rv = emvl2Impl.processCVM(); 85 | emvl2Impl.getDevice().logf("processCVM:%d\n", rv); 86 | if (rv == emvl2Ret::success) { 87 | rv = emvl2Impl.terminalRiskMng(); 88 | emvl2Impl.getDevice().logf("terminalRiskMng:%d\n", rv); 89 | if (rv == emvl2Ret::success) { 90 | rv = emvl2Impl.termActionAnalysis(&termDecision); 91 | emvl2Impl.getDevice().logf("termActionAnalysis:%d\n", rv); 92 | if (rv == emvl2Ret::success) { 93 | rv = emvl2Impl.genAC1(termDecision, &cardDecision); 94 | emvl2Impl.getDevice().logf("genAC1:%d\n", rv); 95 | if (rv == emvl2Ret::success) { 96 | emvl2Impl.getDevice().logf("AC is generated successfully\n"); 97 | 98 | switch (cardDecision) 99 | { 100 | case AAC: 101 | rv = emvl2Ret::aac; 102 | break; 103 | case TC: 104 | rv = emvl2Ret::tc; 105 | break; 106 | case ARQC: 107 | rv = emvl2Ret::arqc; 108 | break; 109 | case AAR: 110 | rv = emvl2Ret::aar; 111 | break; 112 | default: 113 | break; 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | return rv; 127 | } 128 | 129 | EMVL2EXPORT int emvl2Completion(bool isHostReject, uint8_t* Decision, uint8_t* AdviceReversal) { 130 | EmvL2Impl& emvl2Impl = EmvL2Impl::getInstance(); 131 | 132 | emvl2Impl.getDevice().logf("Compliting Transaction\n"); 133 | 134 | return emvl2Impl.genAC2(isHostReject, Decision, AdviceReversal); 135 | } 136 | 137 | EMVL2EXPORT int emvl2CardReset() { 138 | EmvL2Repo& repo = EmvL2Repo::getInstance(); 139 | return EmvL2Impl::getInstance().getDevice().cardReset(); 140 | } 141 | 142 | EMVL2EXPORT int emvl2ApplicationSelection() { 143 | return EmvL2Impl::getInstance().applicationSelection(); 144 | } 145 | 146 | EMVL2EXPORT int emvl2Gpo(uint8_t tType, uint8_t aType, const char* amount, const char* otherAmount) { 147 | return EmvL2Impl::getInstance().gpo(tType, aType, amount, otherAmount); 148 | } 149 | 150 | EMVL2EXPORT int emvl2ReadAppData() { 151 | return EmvL2Impl::getInstance().readAppData(); 152 | } 153 | 154 | EMVL2EXPORT int emvl2OfflineDataAuth() { 155 | return EmvL2Impl::getInstance().offlineDataAuth(); 156 | } 157 | 158 | EMVL2EXPORT int emvl2ProcessRestrict() { 159 | return EmvL2Impl::getInstance().processRestrict(); 160 | } 161 | 162 | EMVL2EXPORT int emvl2ProcessCVM() { 163 | return EmvL2Impl::getInstance().processCVM(); 164 | } 165 | 166 | EMVL2EXPORT int emvl2TerminalRiskMng() { 167 | return EmvL2Impl::getInstance().terminalRiskMng(); 168 | } 169 | 170 | EMVL2EXPORT int emvl2TermActionAnalysis(uint8_t* terminalDecision) { 171 | return EmvL2Impl::getInstance().termActionAnalysis(terminalDecision); 172 | } 173 | 174 | EMVL2EXPORT int emvl2GenAC1(uint8_t terminalDecision, uint8_t* cardDecision) { 175 | int rv = EmvL2Impl::getInstance().genAC1(terminalDecision, cardDecision); 176 | return rv; 177 | } 178 | 179 | EMVL2EXPORT int emvl2GenAC2(bool isHostReject, uint8_t* decision, uint8_t* adviceReversal) { 180 | return EmvL2Impl::getInstance().genAC2(isHostReject, decision, adviceReversal); 181 | } 182 | 183 | EMVL2EXPORT int emvl2GetTag(const uint32_t tag, uint8_t* data, int maxLen) { 184 | int rv = 0; 185 | EmvL2Repo& repo = EmvL2Repo::getInstance(); 186 | Tlv* tlv = repo.getTag(tag); 187 | 188 | if (tlv) { 189 | rv = tlv->len; 190 | if (data) { 191 | if(maxLen > rv) 192 | memcpy(data, tlv->val.data(), rv); 193 | else 194 | memcpy(data, tlv->val.data(), maxLen); 195 | } 196 | } 197 | 198 | return rv; 199 | } 200 | 201 | -------------------------------------------------------------------------------- /corelib/entrypoint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IDevice.h" 4 | 5 | #ifdef WIN32 6 | #include 7 | #ifdef EMVL2EXPORT_SYMBOLS 8 | #define EMVL2EXPORT extern "C" __declspec(dllexport) 9 | #else 10 | #define EMVL2EXPORT extern "C" __declspec(dllimport) 11 | #endif 12 | #else 13 | #define EMVL2EXPORT extern 14 | #endif 15 | 16 | typedef void(__stdcall* LogCallbackFunction)(const char* log); 17 | typedef const char* (__stdcall* PinCallbackFunction)(); 18 | 19 | EMVL2EXPORT int emvl2Version(); 20 | EMVL2EXPORT int emvl2Init(const char *readerName, LogCallbackFunction logCb, PinCallbackFunction pinCb); 21 | 22 | EMVL2EXPORT int emvl2CardReset(); 23 | EMVL2EXPORT int emvl2ApplicationSelection(); 24 | EMVL2EXPORT int emvl2Gpo(uint8_t tranType, uint8_t accountType, const char* amount, const char* otherAmount); 25 | EMVL2EXPORT int emvl2ReadAppData(); 26 | EMVL2EXPORT int emvl2OfflineDataAuth(); 27 | EMVL2EXPORT int emvl2ProcessRestrict(); 28 | EMVL2EXPORT int emvl2ProcessCVM(); 29 | EMVL2EXPORT int emvl2TerminalRiskMng(); 30 | EMVL2EXPORT int emvl2TermActionAnalysis(uint8_t* terminalDecision); 31 | EMVL2EXPORT int emvl2GenAC1(uint8_t terminalDecision, uint8_t* cardDecision); 32 | EMVL2EXPORT int emvl2GenAC2(bool isHostReject, uint8_t* decision, uint8_t* adviceReversal); 33 | 34 | EMVL2EXPORT int emvl2Start(uint8_t ttype, uint8_t atype, const char* amount, const char* otherAmount); 35 | EMVL2EXPORT int emvl2Completion(bool isHostReject, uint8_t* decision, uint8_t* adviceReversal); 36 | 37 | EMVL2EXPORT int emvl2AddCaKey(const char* rid, uint8_t keyId, const char* modules, const char* exponent); 38 | EMVL2EXPORT int emvl2AddAidPrms(const char* aid, const char* data); 39 | 40 | EMVL2EXPORT int emvl2GetTag(const uint32_t tag, uint8_t* data, int maxLen); -------------------------------------------------------------------------------- /corelib/l1/bigdigits.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" 3 | { 4 | #endif 5 | /* $Id: bigdigits.h $ */ 6 | 7 | /******************** SHORT COPYRIGHT NOTICE************************** 8 | This source code is part of the BigDigits multiple-precision 9 | arithmetic library Version 2.3 originally written by David Ireland, 10 | copyright (c) 2001-11 D.I. Management Services Pty Limited, all rights 11 | reserved. It is provided "as is" with no warranties. You may use 12 | this software under the terms of the full copyright notice 13 | "bigdigitsCopyright.txt" that should have been included with this 14 | library or can be obtained from . 15 | This notice must always be retained in any copy. 16 | ******************* END OF COPYRIGHT NOTICE***************************/ 17 | /* 18 | Last updated: 19 | $Date: 2011-11-11 11:11:11 $ 20 | $Revision: 2.3.0 $ 21 | $Author: dai $ 22 | */ 23 | 24 | 25 | /* Interface to BigDigits "mp" functions */ 26 | #ifndef BIGDIGITS_H_ 27 | #define BIGDIGITS_H_ 1 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | #include "bigdtypes.h" 34 | 35 | #define ISODD(p) (p % 2 == 1) 36 | 37 | /**** USER CONFIGURABLE SECTION ****/ 38 | 39 | /* Define type and size of DIGIT */ 40 | 41 | /* [v2.1] Changed to use C99 exact-width types so it will compile with 64-bit compilers. */ 42 | /* [v2.2] Put macros for exact-width types in separate file "bigdtypes.h" */ 43 | 44 | typedef uint32_t DIGIT_T; 45 | typedef uint16_t HALF_DIGIT_T; 46 | 47 | /* Sizes to match */ 48 | #define MAX_DIGIT 0xffffffffUL 49 | #define MAX_HALF_DIGIT 0xffffUL /* NB 'L' */ 50 | #define BITS_PER_DIGIT 32 51 | #define HIBITMASK 0x80000000UL 52 | 53 | /* [v2.2] added option to avoid allocating temp storage 54 | and use fixed automatic arrays instead. 55 | Define NO_ALLOCS to invoke this. 56 | Only applicable to mp functions. Do not use with bd. 57 | */ 58 | /* Specify the maximum number of digits allowed in a temp mp array 59 | -- ignored unless NO_ALLOCS is defined */ 60 | #ifdef NO_ALLOCS 61 | #define MAX_FIXED_DIGITS (8192 / BITS_PER_DIGIT) 62 | #endif 63 | 64 | /**** END OF USER CONFIGURABLE SECTION ****/ 65 | 66 | /**** OPTIONAL PREPROCESSOR DEFINITIONS ****/ 67 | /* 68 | Choose one of: 69 | USE_SPASM: to use the faster ASM routines (if __asm option is available with your compiler). 70 | USE_64WITH32: to use the 64-bit integers if available (e.g. long long). 71 | default: use default internal routines spDivide and spMultiply. 72 | The USE_SPASM option takes precedence over USE_64WITH32. 73 | */ 74 | 75 | #define BITS_PER_HALF_DIGIT (BITS_PER_DIGIT / 2) 76 | #define BYTES_PER_DIGIT (BITS_PER_DIGIT / 8) 77 | 78 | /* Useful macros */ 79 | #define LOHALF(x) ((DIGIT_T)((x) & MAX_HALF_DIGIT)) 80 | #define HIHALF(x) ((DIGIT_T)((x) >> BITS_PER_HALF_DIGIT & MAX_HALF_DIGIT)) 81 | #define TOHIGH(x) ((DIGIT_T)((x) << BITS_PER_HALF_DIGIT)) 82 | 83 | //#define ISODD(x) ((x) & 0x1) 84 | #define ISEVEN(x) (!ISODD(x)) 85 | 86 | #define mpISODD(x, n) (x[0] & 0x1) 87 | #define mpISEVEN(x, n) (!(x[0] & 0x1)) 88 | 89 | #define mpNEXTBITMASK(mask, n) do{if(mask==1){mask=HIBITMASK;n--;}else{mask>>=1;}}while(0) 90 | 91 | const char *copyright_notice(void); 92 | /* Forces linker to include copyright notice in executable */ 93 | 94 | /* 95 | * Multiple precision calculations 96 | * Using known, equal ndigits 97 | * except where noted 98 | */ 99 | 100 | /*************************/ 101 | /* ARITHMETIC OPERATIONS */ 102 | /*************************/ 103 | 104 | DIGIT_T mpAdd_(DIGIT_T w[], const DIGIT_T u[], const DIGIT_T v[], size_t ndigits); 105 | /* Computes w = u + v, returns carry */ 106 | 107 | DIGIT_T mpSubtract(DIGIT_T w[], const DIGIT_T u[], const DIGIT_T v[], size_t ndigits); 108 | /* Computes w = u - v, returns borrow */ 109 | 110 | int mpMultiply(DIGIT_T w[], const DIGIT_T u[], const DIGIT_T v[], size_t ndigits); 111 | /* Computes product w = u * v 112 | u, v = ndigits long; w = 2 * ndigits long */ 113 | 114 | int mpDivide(DIGIT_T q[], DIGIT_T r[], const DIGIT_T u[], 115 | size_t udigits, DIGIT_T v[], size_t vdigits); 116 | /* Computes quotient q = u / v and remainder r = u mod v 117 | q, r, u = udigits long; v = vdigits long 118 | Warning: Trashes q and r first */ 119 | 120 | int mpModulo(DIGIT_T r[], const DIGIT_T u[], size_t udigits, DIGIT_T v[], size_t vdigits); 121 | /* Computes r = u mod v 122 | u = udigits long; r, v = vdigits long */ 123 | 124 | int mpSquare(DIGIT_T w[], const DIGIT_T x[], size_t ndigits); 125 | /* Computes square w = x^2 126 | x = ndigits long; w = 2 * ndigits long */ 127 | 128 | int mpSqrt(DIGIT_T s[], const DIGIT_T x[], size_t ndigits); 129 | /* Computes integer square root s = floor(sqrt(x)) */ 130 | 131 | /* New in [v2.3] */ 132 | int mpCubeRoot(DIGIT_T s[], const DIGIT_T x[], size_t ndigits); 133 | /* Computes integer cube root s = floor(cuberoot(x)) */ 134 | 135 | //*************************/ 136 | /* COMPARISON OPERATIONS */ 137 | /*************************/ 138 | 139 | int mpEqual(const DIGIT_T a[], const DIGIT_T b[], size_t ndigits); 140 | /* Returns true if a == b, else false */ 141 | 142 | int mpCompare_(const DIGIT_T a[], const DIGIT_T b[], size_t ndigits); 143 | /* Returns sign of (a - b) */ 144 | 145 | int mpIsZero_(const DIGIT_T a[], size_t ndigits); 146 | /* Returns true if a == 0, else false */ 147 | 148 | /****************************/ 149 | /* NUMBER THEORY OPERATIONS */ 150 | /****************************/ 151 | 152 | /* [v2.2] removed `const' restriction on m[] for mpModMult and mpModExp */ 153 | 154 | int mpModMult(DIGIT_T a[], const DIGIT_T x[], const DIGIT_T y[], DIGIT_T m[], size_t ndigits); 155 | /* Computes a = (x * y) mod m */ 156 | 157 | int mpModExp(DIGIT_T y[], const DIGIT_T x[], const DIGIT_T e[], DIGIT_T m[], size_t ndigits); 158 | /* Computes y = x^e mod m */ 159 | 160 | int mpModInv(DIGIT_T inv[], const DIGIT_T u[], const DIGIT_T v[], size_t ndigits); 161 | /* Computes inv = u^-1 mod v */ 162 | 163 | int mpGcd(DIGIT_T g[], const DIGIT_T x[], const DIGIT_T y[], size_t ndigits); 164 | /* Computes g = gcd(x, y) */ 165 | 166 | /* [v2.2] Added Jacobi (and Legendre) symbol fn */ 167 | int mpJacobi(const DIGIT_T a[], const DIGIT_T n[], size_t ndigits); 168 | /* Returns Jacobi(a, n) = {0, +1, -1} */ 169 | 170 | /**********************/ 171 | /* BITWISE OPERATIONS */ 172 | /**********************/ 173 | 174 | size_t mpBitLength(const DIGIT_T a[], size_t ndigits); 175 | /* Returns number of significant bits in a */ 176 | 177 | DIGIT_T mpShiftLeft(DIGIT_T a[], const DIGIT_T b[], size_t x, size_t ndigits); 178 | /* Computes a = b << x */ 179 | 180 | DIGIT_T mpShiftRight(DIGIT_T a[], const DIGIT_T b[], size_t x, size_t ndigits); 181 | /* Computes a = b >> x */ 182 | 183 | void mpXorBits(DIGIT_T a[], const DIGIT_T b[], const DIGIT_T c[], size_t ndigits); 184 | /* Computes bitwise a = b XOR c */ 185 | 186 | void mpOrBits(DIGIT_T a[], const DIGIT_T b[], const DIGIT_T c[], size_t ndigits); 187 | /* Computes bitwise a = b OR c */ 188 | 189 | void mpAndBits(DIGIT_T a[], const DIGIT_T b[], const DIGIT_T c[], size_t ndigits); 190 | /* Computes bitwise a = b AND c */ 191 | 192 | void mpNotBits(DIGIT_T a[], const DIGIT_T b[], size_t ndigits); 193 | /* Computes bitwise a = NOT b */ 194 | 195 | void mpModPowerOf2(DIGIT_T a[], size_t ndigits, size_t L); 196 | /* Computes a = a mod 2^L */ 197 | 198 | int mpSetBit(DIGIT_T a[], size_t ndigits, size_t n, int value); 199 | /* Set bit n (0..nbits-1) with value 1 or 0 */ 200 | 201 | int mpGetBit(DIGIT_T a[], size_t ndigits, size_t n); 202 | /* Returns value 1 or 0 of bit n (0..nbits-1) */ 203 | 204 | /*************************/ 205 | /* ASSIGNMENT OPERATIONS */ 206 | /*************************/ 207 | 208 | volatile DIGIT_T mpSetZero(volatile DIGIT_T a[], size_t ndigits); 209 | /* Sets a = 0 */ 210 | 211 | void mpSetDigit(DIGIT_T a[], DIGIT_T d, size_t ndigits); 212 | /* Sets a = d where d is a single digit */ 213 | 214 | void mpSetEqual(DIGIT_T a[], const DIGIT_T b[], size_t ndigits); 215 | /* Sets a = b */ 216 | 217 | /****************************/ 218 | /* SIGNED INTEGER FUNCTIONS */ 219 | /****************************/ 220 | 221 | int mpIsNegative(const DIGIT_T x[], size_t ndigits); 222 | /* Returns TRUE (1) if x < 0, else FALSE (0) */ 223 | 224 | int mpChs(DIGIT_T x[], const DIGIT_T y[], size_t ndigits); 225 | /* Sets x = -y */ 226 | 227 | int mpAbs(DIGIT_T x[], const DIGIT_T y[], size_t ndigits); 228 | /* Sets x = |y| */ 229 | 230 | 231 | /**********************/ 232 | /* OTHER MP UTILITIES */ 233 | /**********************/ 234 | 235 | size_t mpSizeof(const DIGIT_T a[], size_t ndigits); 236 | /* Returns size i.e. number of significant non-zero digits in a */ 237 | 238 | int mpIsPrime(DIGIT_T w[], size_t ndigits, size_t t); 239 | /* Returns true if w > 2 is a probable prime 240 | t tests using FIPS-186-2/Rabin-Miller */ 241 | 242 | int mpRabinMiller(DIGIT_T w[], size_t ndigits, size_t t); 243 | /* Just the FIPS-186-2/Rabin-Miller test 244 | without trial division by small primes */ 245 | 246 | /**********************************************/ 247 | /* FUNCTIONS THAT OPERATE WITH A SINGLE DIGIT */ 248 | /**********************************************/ 249 | 250 | DIGIT_T mpShortAdd(DIGIT_T w[], const DIGIT_T u[], DIGIT_T d, size_t ndigits); 251 | /* Computes w = u + d, returns carry */ 252 | 253 | DIGIT_T mpShortSub(DIGIT_T w[], const DIGIT_T u[], DIGIT_T d, size_t ndigits); 254 | /* Computes w = u - d, returns borrow */ 255 | 256 | DIGIT_T mpShortMult(DIGIT_T p[], const DIGIT_T x[], DIGIT_T d, size_t ndigits); 257 | /* Computes product p = x * d */ 258 | 259 | DIGIT_T mpShortDiv(DIGIT_T q[], const DIGIT_T u[], DIGIT_T d, size_t ndigits); 260 | /* Computes q = u / d, returns remainder */ 261 | 262 | DIGIT_T mpShortMod(const DIGIT_T a[], DIGIT_T d, size_t ndigits); 263 | /* Returns r = a mod d */ 264 | 265 | int mpShortCmp(const DIGIT_T a[], DIGIT_T d, size_t ndigits); 266 | /* Returns sign of (a - d) where d is a single digit */ 267 | 268 | /**************************************/ 269 | /* CORE SINGLE PRECISION CALCULATIONS */ 270 | /* (double where necessary) */ 271 | /**************************************/ 272 | 273 | /* NOTE spMultiply and spDivide are used by almost all mp functions. 274 | Using the Intel MASM alternatives gives significant speed improvements 275 | -- to use, define USE_SPASM as a preprocessor directive. 276 | [v2.2] Removed references to spasm* versions. 277 | */ 278 | 279 | int spMultiply(DIGIT_T p[2], DIGIT_T x, DIGIT_T y); 280 | /* Computes p = x * y */ 281 | 282 | DIGIT_T spDivide(DIGIT_T *q, DIGIT_T *r, const DIGIT_T u[2], DIGIT_T v); 283 | /* Computes quotient q = u / v, remainder r = u mod v */ 284 | 285 | /****************************/ 286 | /* RANDOM NUMBER FUNCTIONS */ 287 | /* CAUTION: NOT thread-safe */ 288 | /****************************/ 289 | 290 | DIGIT_T spSimpleRand(DIGIT_T lower, DIGIT_T upper); 291 | /* Returns a simple pseudo-random digit between lower and upper */ 292 | 293 | /* [Version 2.1: spBetterRand moved to spRandom.h] */ 294 | 295 | /*******************/ 296 | /* PRINT UTILITIES */ 297 | /*******************/ 298 | 299 | void mpPrint(const DIGIT_T *p, size_t len); 300 | /* Print all digits incl leading zero digits */ 301 | void mpPrintNL(const DIGIT_T *p, size_t len); 302 | /* Print all digits with newlines */ 303 | void mpPrintTrim(const DIGIT_T *p, size_t len); 304 | /* Print but trim leading zero digits */ 305 | void mpPrintTrimNL(const DIGIT_T *p, size_t len); 306 | /* Print, trim leading zeroes, add newlines */ 307 | 308 | /* New in [v2.3] */ 309 | void mpPrintHex(const char *prefix, const DIGIT_T *p, size_t len, const char *suffix); 310 | /* Print in hex format with optional prefix and suffix strings */ 311 | void mpPrintDecimal(const char *prefix, const DIGIT_T *p, size_t len, const char *suffix); 312 | /* Print in decimal format with optional prefix and suffix strings */ 313 | 314 | /************************/ 315 | /* CONVERSION UTILITIES */ 316 | /************************/ 317 | 318 | size_t mpConvFromOctets(DIGIT_T a[], size_t ndigits, const unsigned char *c, size_t nbytes); 319 | /* Converts nbytes octets into big digit a of max size ndigits 320 | Returns actual number of digits set */ 321 | size_t mpConvToOctets(const DIGIT_T a[], size_t ndigits, unsigned char *c, size_t nbytes); 322 | /* Convert big digit a into string of octets, in big-endian order, 323 | padding to nbytes or truncating if necessary. 324 | Return number of non-zero octets required. */ 325 | size_t mpConvFromDecimal(DIGIT_T a[], size_t ndigits, const char *s); 326 | /* Convert a string in decimal format to a big digit. 327 | Return actual number of (possibly zero) digits set. */ 328 | size_t mpConvToDecimal(const DIGIT_T a[], size_t ndigits, char *s, size_t smax); 329 | /* Convert big digit a into a string in decimal format, 330 | where s has size smax including the terminating zero. 331 | Return number of chars required excluding leading zeroes. */ 332 | size_t mpConvFromHex(DIGIT_T a[], size_t ndigits, const char *s); 333 | /* Convert a string in hexadecimal format to a big digit. 334 | Return actual number of (possibly zero) digits set. */ 335 | size_t mpConvToHex(const DIGIT_T a[], size_t ndigits, char *s, size_t smax); 336 | /* Convert big digit a into a string in hexadecimal format, 337 | where s has size smax including the terminating zero. 338 | Return number of chars required excluding leading zeroes. */ 339 | 340 | /****************/ 341 | /* VERSION INFO */ 342 | /****************/ 343 | int mpVersion(void); 344 | /* Returns version number = major*1000+minor*100+release*10+uses_asm(0|1)+uses_64(0|2)+uses_noalloc(0|5) 345 | E.g. Version 2.3.0 will return 230x where x denotes the preprocessor options 346 | x | USE_SPASM | USE_64WITH32 | NO_ALLOCS 347 | ---------------------------------------- 348 | 0 No No No 349 | 1 Yes No No 350 | 2 No Yes No 351 | 3 Yes Yes* No 352 | 5 No No Yes 353 | 6 Yes No Yes 354 | 7 No Yes Yes 355 | 8 Yes Yes* Yes 356 | ---------------------------------------- 357 | * USE_SPASM will take precedence over USE_64WITH32. 358 | */ 359 | 360 | /*************************************************/ 361 | /* MEMORY ALLOCATION FUNCTIONS - USED INTERNALLY */ 362 | /*************************************************/ 363 | /* [v2.2] added option to avoid memory allocation if NO_ALLOCS is defined */ 364 | #ifndef NO_ALLOCS 365 | DIGIT_T *mpAlloc(size_t ndigits); 366 | void mpFree(DIGIT_T **p); 367 | #endif 368 | void mpFail(char *msg); 369 | 370 | /* Clean up by zeroising and freeing allocated memory */ 371 | #ifdef NO_ALLOCS 372 | #define mpDESTROY(b, n) do{if(b)mpSetZero(b,n);}while(0) 373 | #else 374 | #define mpDESTROY(b, n) do{if(b)mpSetZero(b,n);mpFree(&b);}while(0) 375 | #endif 376 | 377 | #ifdef __cplusplus 378 | } 379 | #endif 380 | 381 | #endif /* BIGDIGITS_H_ */ 382 | #ifdef __cplusplus 383 | } 384 | #endif -------------------------------------------------------------------------------- /corelib/l1/bigdtypes.h: -------------------------------------------------------------------------------- 1 | /* $Id: bigdtypes.h $ */ 2 | 3 | /******************** SHORT COPYRIGHT NOTICE************************** 4 | This source code is part of the BigDigits multiple-precision 5 | arithmetic library Version 2.3 originally written by David Ireland, 6 | copyright (c) 2001-11 D.I. Management Services Pty Limited, all rights 7 | reserved. It is provided "as is" with no warranties. You may use 8 | this software under the terms of the full copyright notice 9 | "bigdigitsCopyright.txt" that should have been included with this 10 | library or can be obtained from . 11 | This notice must always be retained in any copy. 12 | ******************* END OF COPYRIGHT NOTICE***************************/ 13 | /* 14 | Last updated: 15 | $Date: 2011-11-11 11:11:11 $ 16 | $Revision: 2.3.0 $ 17 | $Author: dai $ 18 | */ 19 | 20 | #ifndef BIGDTYPES_H_ 21 | #define BIGDTYPES_H_ 1 22 | 23 | #include 24 | 25 | /* 26 | The following PP instructions assume that all Linux systems have a C99-conforming 27 | ; that other Unix systems have the uint32_t definitions in ; 28 | and that MS et al don't have them at all. This version assumes that a long is 32 bits. 29 | Adjust if necessary to suit your system. 30 | You can override by defining HAVE_C99INCLUDES or HAVE_SYS_TYPES. 31 | */ 32 | 33 | #ifndef EXACT_INTS_DEFINED_ 34 | #define EXACT_INTS_DEFINED_ 1 35 | #ifndef HAVE_C99INCLUDES 36 | #if (__STDC_VERSION >= 199901L) || defined(linux) || defined(__linux__) 37 | #define HAVE_C99INCLUDES 38 | #endif 39 | #endif 40 | #ifndef HAVE_SYS_TYPES 41 | #if defined(unix) || defined(__unix__) 42 | #define HAVE_SYS_TYPES 43 | #endif 44 | #endif 45 | #ifdef HAVE_C99INCLUDES 46 | #include 47 | #elif defined(HAVE_SYS_TYPES) 48 | #include 49 | #else 50 | #define uint32_t unsigned long 51 | #define uint16_t unsigned short 52 | #endif /* HAVE_C99INCLUDES */ 53 | #endif /* EXACT_INTS_DEFINED_ */ 54 | 55 | /* Macros for format specifiers 56 | -- change to "u", "x" and "X" if necessary */ 57 | #ifdef HAVE_C99INCLUDES 58 | #include 59 | #else 60 | #define PRIu32 "lu" 61 | #define PRIx32 "lx" 62 | #define PRIX32 "lX" 63 | #endif 64 | /* We define our own */ 65 | #define PRIuBIGD PRIu32 66 | #define PRIxBIGD PRIx32 67 | #define PRIXBIGD PRIX32 68 | 69 | #endif /* BIGDTYPES_H_ */ 70 | -------------------------------------------------------------------------------- /corelib/l1/deviceImpl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "bigdigits.h" 8 | #include "sha1.h" 9 | 10 | #ifdef WIN32 11 | 12 | #include 13 | #pragma comment(lib, "winscard.lib") 14 | 15 | #endif 16 | 17 | #include "DeviceImpl.h" 18 | 19 | using namespace std; 20 | 21 | void memreverse(uint8_t * indata, uint8_t * outdata, int inlen) 22 | { 23 | int i; 24 | for (i = 0; i < inlen; i++) 25 | { 26 | outdata[i] = indata[inlen - i - 1]; 27 | } 28 | } 29 | 30 | DeviceImpl::DeviceImpl() { 31 | 32 | } 33 | 34 | DeviceImpl::DeviceImpl(const char* rn, LogCallbackFunction logCb, PinCallbackFunction pinCb) { 35 | readerName = rn; 36 | logCallback = logCb; 37 | pinCallback = pinCb; 38 | } 39 | 40 | DeviceImpl::~DeviceImpl() { 41 | 42 | } 43 | 44 | emvl2Ret DeviceImpl::cardReset() { 45 | DWORD dwLength = 300; 46 | DWORD dwCardState = 0; 47 | DWORD dwActiveProtocol = 0; 48 | DWORD dwShareMode = SCARD_SHARE_SHARED; 49 | DWORD dwPreferedProtocol = SCARD_PROTOCOL_Tx; 50 | DWORD dwATRLength = 40; 51 | 52 | unsigned char* atrData = { 0 }; 53 | 54 | emvl2Ret rv = failure; 55 | 56 | int ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &cardContext); 57 | if (ret == SCARD_S_SUCCESS) 58 | { 59 | ret = SCardConnectA(cardContext, readerName.c_str(), dwShareMode, dwPreferedProtocol, &card, &dwActiveProtocol); 60 | if (ret == SCARD_S_SUCCESS) 61 | { 62 | ret = SCardStatusA(card, (char*)readerName.c_str(), &dwLength, &dwCardState, &dwActiveProtocol, atrData, &dwATRLength); 63 | if (ret == SCARD_S_SUCCESS) 64 | { 65 | rv = success; 66 | 67 | atrData = (unsigned char*)malloc(dwATRLength); 68 | 69 | actIOProtocol.cbPciLength = sizeof(SCARD_IO_REQUEST); 70 | 71 | if (dwActiveProtocol == SCARD_PROTOCOL_T0) 72 | { 73 | protocol = 0; 74 | actIOProtocol.dwProtocol = (ULONG_PTR)SCARD_PCI_T0; 75 | } 76 | else if (dwActiveProtocol == SCARD_PROTOCOL_T1) 77 | { 78 | protocol = 1; 79 | actIOProtocol.dwProtocol = (ULONG_PTR)SCARD_PCI_T1; 80 | } 81 | else 82 | { 83 | rv = fallback; 84 | } 85 | 86 | } 87 | } 88 | } 89 | 90 | return rv; 91 | } 92 | 93 | emvl2Ret DeviceImpl::cardSendReceive(uint8_t* sendData, uint32_t sendDataLength, uint8_t* replyData, uint32_t* replyDataLength) { 94 | emvl2Ret rv = failure; 95 | int nRet = -1; 96 | 97 | DWORD dwOutLen = 258; 98 | 99 | logf("APDU Send:\n"); 100 | hexdump(sendData, sendDataLength); 101 | 102 | nRet = SCardTransmit(card 103 | , (LPSCARD_IO_REQUEST)(UINT_PTR)actIOProtocol.dwProtocol 104 | , sendData 105 | , sendDataLength 106 | , NULL 107 | , replyData 108 | , &dwOutLen 109 | ); 110 | 111 | if (nRet == SCARD_S_SUCCESS) 112 | { 113 | nRet = 0; 114 | *replyDataLength = dwOutLen; 115 | rv = success; 116 | 117 | logf("APDU Recv:\n"); 118 | hexdump(replyData, dwOutLen); 119 | } 120 | 121 | return rv; 122 | } 123 | 124 | uint8_t DeviceImpl::cardProtocol() { 125 | return protocol; 126 | } 127 | 128 | emvl2Ret DeviceImpl::pinOfflinePlain(uint8_t *cardSws) { 129 | if (!pinCallback) 130 | return success; 131 | 132 | const char* pin = pinCallback(); 133 | this->logf("Offline Plain Pin Entered: %s\n", pin); 134 | 135 | uint8_t sendBuff[13] = { 0 }; 136 | int sendLength = 0; 137 | 138 | uint8_t recvBuff[2] = { 0 }; 139 | uint32_t recvLength = 0; 140 | 141 | char pinBlockData[16] = { 0 }; 142 | int index = 0; 143 | 144 | memset(&pinBlockData[2], 'F', sizeof(pinBlockData) - 2); 145 | pinBlockData[0] = '2'; 146 | pinBlockData[1] = 0x30 + (uint8_t)strlen(pin); 147 | memcpy(&pinBlockData[2], pin, strlen(pin)); 148 | 149 | index = 0; 150 | sendBuff[index] = 0x00; 151 | index++; 152 | sendBuff[index] = 0x20; 153 | index++; 154 | sendBuff[index] = 0x00; 155 | index++; 156 | sendBuff[index] = 0x80; 157 | index++; 158 | sendBuff[index] = 0x08; 159 | index++; 160 | 161 | convertStrToBcd(pinBlockData, strlen(pinBlockData) / 2, &sendBuff[index], 0); 162 | 163 | index += sizeof(pinBlockData) / 2; 164 | 165 | sendLength = index; 166 | cardSendReceive(sendBuff, sendLength, recvBuff, &recvLength); 167 | memcpy(cardSws, recvBuff, 2); 168 | return success; 169 | } 170 | 171 | emvl2Ret DeviceImpl::pinOfflineEncrypted(uint8_t* publicKeyMod, uint8_t publicKeyModLength, uint8_t* publicKeyExponent, uint8_t publicKeyExponentLength, uint8_t unpredictNumber[8], uint8_t *cardSws) { 172 | if (!pinCallback) 173 | return success; 174 | 175 | const char* pin = pinCallback(); 176 | this->logf("Offline Encrypted Pin Entered: %s\n", pin); 177 | 178 | int index = 0; 179 | char pinBlockData[16] = { 0 }; 180 | uint8_t ucPINEncData[256]; 181 | uint8_t sendBuff[256] = { 0 }; 182 | int sendLength = 0; 183 | uint8_t recvBuff[2] = { 0 }; 184 | uint32_t recvLength = 0; 185 | 186 | memset(&pinBlockData[2], 'F', sizeof(pinBlockData) - 2); 187 | pinBlockData[0] = '2'; 188 | pinBlockData[1] = 0x30 + (uint8_t)strlen(pin); 189 | memcpy(&pinBlockData[2], pin, strlen(pin)); 190 | 191 | ucPINEncData[0] = 0x7F; 192 | convertStrToBcd(pinBlockData, strlen(pinBlockData) / 2, ucPINEncData + 1, 0); 193 | memcpy(ucPINEncData + 9, unpredictNumber, 8); 194 | memset(&ucPINEncData[17], 0x74, publicKeyModLength - 17); 195 | 196 | index = 0; 197 | sendBuff[index] = 0x00; 198 | index++; 199 | sendBuff[index] = 0x20; 200 | index++; 201 | sendBuff[index] = 0x00; 202 | index++; 203 | sendBuff[index] = 0x88; 204 | index++; 205 | sendBuff[index] = publicKeyModLength; 206 | index++; 207 | 208 | rsaDecrypt(publicKeyMod, publicKeyModLength, publicKeyExponent, publicKeyExponentLength, ucPINEncData, publicKeyModLength, &sendBuff[index]); 209 | 210 | index += publicKeyModLength; 211 | sendLength = index; 212 | cardSendReceive(sendBuff, sendLength, recvBuff, &recvLength); 213 | memcpy(cardSws, recvBuff, 2); 214 | 215 | return success; 216 | } 217 | 218 | emvl2Ret DeviceImpl::pinOnline(uint8_t* pan, uint8_t len) { 219 | if (!pinCallback) 220 | return success; 221 | 222 | const char* pin = pinCallback(); 223 | this->logf("Online Pin Entered: %s\n", pin); 224 | return success; 225 | } 226 | 227 | emvl2Ret DeviceImpl::getDateTime(emvl2DateTime* datetime) { 228 | time_t t = std::time(0); 229 | tm* now = std::localtime(&t); 230 | 231 | datetime->year = now->tm_year + 1900; 232 | datetime->month = now->tm_mon + 1; 233 | datetime->day = now->tm_mday; 234 | datetime->hour = now->tm_hour; 235 | datetime->minute = now->tm_min; 236 | datetime->second = now->tm_sec; 237 | 238 | logf("DateTime: %02d/%02d/%04d %02d:%02d:%02d\n", datetime->day, datetime->month, datetime->year, datetime->hour, datetime->minute, datetime->second); 239 | return success; 240 | } 241 | 242 | void DeviceImpl::logf(const char* format, ...) { 243 | char dest[1024 * 16]; 244 | va_list argptr; 245 | va_start(argptr, format); 246 | vsprintf(dest, format, argptr); 247 | va_end(argptr); 248 | 249 | std::cout << dest; 250 | if (logCallback) { 251 | logCallback(dest); 252 | } 253 | 254 | //fstream fd; 255 | //fd.open("kernel.log", ios::out | ios::app); // Open the file in append mode 256 | //if (!fd) 257 | // cout << "No such file found" << endl; 258 | //else { 259 | // fd << dest; 260 | // fd.close(); 261 | //} 262 | } 263 | 264 | emvl2Ret DeviceImpl::sha1(uint8_t* data, uint32_t length, uint8_t digest[20]) { 265 | compute_hash_str(data, length, digest); 266 | return success; 267 | } 268 | 269 | emvl2Ret DeviceImpl::rsaDecrypt(uint8_t* modulus, uint8_t modulusLength, uint8_t* exponent, uint8_t exponentLength, uint8_t* inputData, uint8_t inputDataLength, uint8_t* decryptedData) { 270 | #define OCTETS_PER_DIGIT sizeof(unsigned long) 271 | 272 | int nret; 273 | const int nsize = (modulusLength + OCTETS_PER_DIGIT - 1) / OCTETS_PER_DIGIT; 274 | const int nadjustment = nsize * OCTETS_PER_DIGIT - modulusLength; 275 | uint8_t* pucExpTemp = 0, * pucDataRev = 0, * pucModuleRev = 0, * pucPlainData = 0; 276 | 277 | if (!modulus) { 278 | return failure; 279 | } 280 | 281 | pucExpTemp = (uint8_t*)malloc(nsize * OCTETS_PER_DIGIT); 282 | pucDataRev = (uint8_t*)malloc(nsize * OCTETS_PER_DIGIT); 283 | pucPlainData = (uint8_t*)malloc(nsize * OCTETS_PER_DIGIT); 284 | pucModuleRev = (uint8_t*)malloc(nsize * OCTETS_PER_DIGIT); 285 | 286 | if (!pucExpTemp || !pucDataRev || !pucPlainData || !pucModuleRev) 287 | { 288 | free(pucExpTemp); 289 | free(pucDataRev); 290 | free(pucPlainData); 291 | free(pucModuleRev); 292 | return failure; 293 | } 294 | 295 | memset(pucDataRev, 0, nsize * OCTETS_PER_DIGIT); 296 | memset(pucModuleRev, 0, nsize * OCTETS_PER_DIGIT); 297 | 298 | memreverse(modulus, pucModuleRev, nsize * OCTETS_PER_DIGIT - nadjustment); 299 | memreverse(inputData, pucDataRev, nsize * OCTETS_PER_DIGIT - nadjustment); 300 | memset(pucExpTemp, 0, nsize * OCTETS_PER_DIGIT); 301 | memcpy(pucExpTemp, exponent, exponentLength); 302 | 303 | if (0 == (nret = mpModExp((DIGIT_T*)pucPlainData, (DIGIT_T*)pucDataRev, (DIGIT_T*)pucExpTemp, (DIGIT_T*)pucModuleRev, nsize))) 304 | { 305 | memreverse(pucPlainData, decryptedData, modulusLength); 306 | } 307 | 308 | free(pucExpTemp); 309 | free(pucDataRev); 310 | free(pucPlainData); 311 | free(pucModuleRev); 312 | 313 | if (nret) 314 | { 315 | return failure; 316 | } 317 | 318 | return success; 319 | } 320 | 321 | emvl2Ret DeviceImpl::genRand(uint8_t* unpredictNumber, int len) { 322 | srand((unsigned int)time(NULL)); 323 | 324 | for (int i = 0; i < len; i++) 325 | { 326 | unpredictNumber[i] = rand(); 327 | } 328 | 329 | return success; 330 | } 331 | 332 | void DeviceImpl::hexdump(void* ptr, int buflen) { 333 | if (!ptr) 334 | return; 335 | 336 | unsigned char* buf = (unsigned char*)ptr; 337 | int i, j; 338 | for (i = 0; i < buflen; i += 16) { 339 | logf("%06x: ", i); 340 | for (j = 0; j < 16; j++) 341 | if (i + j < buflen) 342 | logf("%02x ", buf[i + j]); 343 | else 344 | logf(" "); 345 | logf(" "); 346 | for (j = 0; j < 16; j++) 347 | if (i + j < buflen) 348 | logf("%c", isprint(buf[i + j]) ? buf[i + j] : '.'); 349 | logf("\n"); 350 | } 351 | } 352 | 353 | void DeviceImpl::convertStrToBcd(const char* src, int len, uint8_t* dest, int RL) 354 | { 355 | int i = 0, strLen = strlen(src); 356 | uint8_t subVal; 357 | strLen = strLen > len * 2 ? len * 2 : strLen; 358 | memset(dest, 0, len); 359 | if (RL == 1) 360 | { 361 | for (i = 0; i < strLen; i++) 362 | { 363 | subVal = (*(src + (strLen - i))) >= (uint8_t)'A' ? 0x37 : 0x30; 364 | *(dest + (i / 2)) = i % 2 != 0 ? ((*((uint8_t*)(src + i)) - subVal) & 0x0F) | (*(dest + (i / 2))) : (((*((uint8_t*)(src + i)) - subVal) & 0x0F) << 4); 365 | } 366 | } 367 | else if (RL == 0) 368 | { 369 | len--; 370 | for (i = 1; i <= strLen; i++) 371 | { 372 | subVal = (*(src + (strLen - i))) >= (uint8_t)'A' ? 0x37 : 0x30; 373 | *(dest + (len)) = i % 2 != 0 ? ((*((uint8_t*)(src + (strLen - i))) - subVal) & 0x0F) : (((*((uint8_t*)(src + (strLen - i))) - subVal) & 0x0F) << 4) | (*(dest + (len))); 374 | 375 | if (i % 2 == 0) 376 | { 377 | len--; 378 | } 379 | } 380 | } 381 | } -------------------------------------------------------------------------------- /corelib/l1/deviceImpl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "..\IDevice.h" 3 | #include "../entrypoint.h" 4 | #include 5 | 6 | using namespace std; 7 | 8 | class DeviceImpl : public IDevice { 9 | public: 10 | DeviceImpl(); 11 | DeviceImpl(const char *readerName, LogCallbackFunction logCb, PinCallbackFunction pinCb); 12 | ~DeviceImpl(); 13 | 14 | emvl2Ret cardReset(); 15 | emvl2Ret cardSendReceive(uint8_t* sendData, uint32_t sendDataLength, uint8_t* replyData, uint32_t* replyDataLength); 16 | uint8_t cardProtocol(); 17 | emvl2Ret pinOfflinePlain(uint8_t *cardSws); 18 | emvl2Ret pinOfflineEncrypted(uint8_t* publicKeyMod, uint8_t publicKeyModLength, uint8_t* publicKeyExponent, uint8_t publicKeyExponentLength, uint8_t unpredictNumber[8], uint8_t *cardSws); 19 | emvl2Ret pinOnline(uint8_t* pan, uint8_t len); 20 | emvl2Ret getDateTime(emvl2DateTime* datetime); 21 | void logf(const char* format, ...); 22 | emvl2Ret sha1(uint8_t* data, uint32_t length, uint8_t digest[20]); 23 | emvl2Ret rsaDecrypt(uint8_t* modulus, uint8_t modulusLength, uint8_t* exponent, uint8_t exponentLength, uint8_t* inputData, uint8_t inputDataLength, uint8_t* decryptedData); 24 | emvl2Ret genRand(uint8_t* unpredictNumber, int len); 25 | void hexdump(void* ptr, int buflen); 26 | 27 | private: 28 | void convertStrToBcd(const char* src, int len, uint8_t* dest, int RL); 29 | 30 | 31 | private: 32 | SCARD_IO_REQUEST actIOProtocol; 33 | SCARDHANDLE card; 34 | SCARDCONTEXT cardContext; 35 | uint8_t protocol; 36 | std::string readerName = "Gemplus USB Smart Card Reader 0"; 37 | LogCallbackFunction logCallback = NULL; 38 | PinCallbackFunction pinCallback = NULL; 39 | }; -------------------------------------------------------------------------------- /corelib/l1/sha1.c: -------------------------------------------------------------------------------- 1 | #include "sha1.h" 2 | #include 3 | 4 | typedef struct 5 | { 6 | uint32_t state[5]; 7 | uint32_t count[2]; 8 | uint8_t buffer[64]; 9 | } SHA1_CTX; 10 | 11 | void SHA1Transform(uint32_t state[5], uint8_t buffer[64]); 12 | void SHA1Init(SHA1_CTX *context); 13 | void SHA1Update(SHA1_CTX *context, uint8_t *data, uint32_t len); 14 | void SHA1Final(uint8_t digest[20], SHA1_CTX *context); 15 | 16 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 17 | 18 | /* blk0() and blk() perform the initial expand. */ 19 | /* I got the idea of expanding during the round function from SSLeay */ 20 | 21 | #ifdef WIN32 22 | #define LITTLE_ENDIAN 23 | #endif 24 | #ifdef LITTLE_ENDIAN 25 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) |(rol(block->l[i],8)&0x00FF00FF)) 26 | #else 27 | #define blk0(i) block->l[i] 28 | #endif 29 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15]^block->l[(i+2)&15]^block->l[i&15],1)) 30 | 31 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 32 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 33 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 34 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 35 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 36 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 37 | 38 | extern void compute_hash_str(uint8_t *buffer, int bufferlen, uint8_t digest[20]) 39 | { 40 | #ifdef WIN32 41 | SHA1_CTX context; 42 | #else 43 | HAL_ret_t hal_ret; 44 | #endif 45 | 46 | 47 | SHA1Init(&context); 48 | SHA1Update(&context, buffer, bufferlen); 49 | SHA1Final(digest, &context); 50 | 51 | } 52 | 53 | void SHA1Transform(uint32_t state[5], uint8_t buffer[64]) 54 | { 55 | uint32_t a, b, c, d, e; 56 | typedef union 57 | { 58 | uint8_t c[64]; 59 | uint32_t l[16]; 60 | } CHAR64LONG16; 61 | CHAR64LONG16 *block; 62 | block = (CHAR64LONG16 *)buffer; 63 | /* Copy context->state[] to working vars */ 64 | a = state[0]; 65 | b = state[1]; 66 | c = state[2]; 67 | d = state[3]; 68 | e = state[4]; 69 | /* 4 rounds of 20 operations each. Loop unrolled. */ 70 | R0(a, b, c, d, e, 0); 71 | R0(e, a, b, c, d, 1); 72 | R0(d, e, a, b, c, 2); 73 | R0(c, d, e, a, b, 3); 74 | R0(b, c, d, e, a, 4); 75 | R0(a, b, c, d, e, 5); 76 | R0(e, a, b, c, d, 6); 77 | R0(d, e, a, b, c, 7); 78 | R0(c, d, e, a, b, 8); 79 | R0(b, c, d, e, a, 9); 80 | R0(a, b, c, d, e, 10); 81 | R0(e, a, b, c, d, 11); 82 | R0(d, e, a, b, c, 12); 83 | R0(c, d, e, a, b, 13); 84 | R0(b, c, d, e, a, 14); 85 | R0(a, b, c, d, e, 15); 86 | R1(e, a, b, c, d, 16); 87 | R1(d, e, a, b, c, 17); 88 | R1(c, d, e, a, b, 18); 89 | R1(b, c, d, e, a, 19); 90 | R2(a, b, c, d, e, 20); 91 | R2(e, a, b, c, d, 21); 92 | R2(d, e, a, b, c, 22); 93 | R2(c, d, e, a, b, 23); 94 | R2(b, c, d, e, a, 24); 95 | R2(a, b, c, d, e, 25); 96 | R2(e, a, b, c, d, 26); 97 | R2(d, e, a, b, c, 27); 98 | R2(c, d, e, a, b, 28); 99 | R2(b, c, d, e, a, 29); 100 | R2(a, b, c, d, e, 30); 101 | R2(e, a, b, c, d, 31); 102 | R2(d, e, a, b, c, 32); 103 | R2(c, d, e, a, b, 33); 104 | R2(b, c, d, e, a, 34); 105 | R2(a, b, c, d, e, 35); 106 | R2(e, a, b, c, d, 36); 107 | R2(d, e, a, b, c, 37); 108 | R2(c, d, e, a, b, 38); 109 | R2(b, c, d, e, a, 39); 110 | R3(a, b, c, d, e, 40); 111 | R3(e, a, b, c, d, 41); 112 | R3(d, e, a, b, c, 42); 113 | R3(c, d, e, a, b, 43); 114 | R3(b, c, d, e, a, 44); 115 | R3(a, b, c, d, e, 45); 116 | R3(e, a, b, c, d, 46); 117 | R3(d, e, a, b, c, 47); 118 | R3(c, d, e, a, b, 48); 119 | R3(b, c, d, e, a, 49); 120 | R3(a, b, c, d, e, 50); 121 | R3(e, a, b, c, d, 51); 122 | R3(d, e, a, b, c, 52); 123 | R3(c, d, e, a, b, 53); 124 | R3(b, c, d, e, a, 54); 125 | R3(a, b, c, d, e, 55); 126 | R3(e, a, b, c, d, 56); 127 | R3(d, e, a, b, c, 57); 128 | R3(c, d, e, a, b, 58); 129 | R3(b, c, d, e, a, 59); 130 | R4(a, b, c, d, e, 60); 131 | R4(e, a, b, c, d, 61); 132 | R4(d, e, a, b, c, 62); 133 | R4(c, d, e, a, b, 63); 134 | R4(b, c, d, e, a, 64); 135 | R4(a, b, c, d, e, 65); 136 | R4(e, a, b, c, d, 66); 137 | R4(d, e, a, b, c, 67); 138 | R4(c, d, e, a, b, 68); 139 | R4(b, c, d, e, a, 69); 140 | R4(a, b, c, d, e, 70); 141 | R4(e, a, b, c, d, 71); 142 | R4(d, e, a, b, c, 72); 143 | R4(c, d, e, a, b, 73); 144 | R4(b, c, d, e, a, 74); 145 | R4(a, b, c, d, e, 75); 146 | R4(e, a, b, c, d, 76); 147 | R4(d, e, a, b, c, 77); 148 | R4(c, d, e, a, b, 78); 149 | R4(b, c, d, e, a, 79); 150 | /* Add the working vars back into context.state[] */ 151 | state[0] += a; 152 | state[1] += b; 153 | state[2] += c; 154 | state[3] += d; 155 | state[4] += e; 156 | /* Wipe variables */ 157 | a = b = c = d = e = 0; 158 | } 159 | 160 | void SHA1Init(SHA1_CTX *context) 161 | { 162 | /* SHA1 initialization constants */ 163 | context->state[0] = 0x67452301; 164 | context->state[1] = 0xEFCDAB89; 165 | context->state[2] = 0x98BADCFE; 166 | context->state[3] = 0x10325476; 167 | context->state[4] = 0xC3D2E1F0; 168 | context->count[0] = context->count[1] = 0; 169 | } 170 | 171 | void SHA1Update(SHA1_CTX *context, uint8_t *data, uint32_t len) 172 | { 173 | uint32_t i, j; 174 | j = (context->count[0] >> 3) & 63; 175 | 176 | if((context->count[0] += len << 3) < (len << 3)) 177 | { 178 | context->count[1]++; 179 | } 180 | 181 | context->count[1] += (len >> 29); 182 | 183 | if((j + len) > 63) 184 | { 185 | memcpy(&context->buffer[j], data, (i = 64 - j)); 186 | SHA1Transform(context->state, context->buffer); 187 | 188 | for(; i + 63 < len; i += 64) 189 | { 190 | SHA1Transform(context->state, &data[i]); 191 | } 192 | 193 | j = 0; 194 | } 195 | else 196 | { 197 | i = 0; 198 | } 199 | 200 | memcpy(&context->buffer[j], &data[i], len - i); 201 | } 202 | 203 | void SHA1Final(uint8_t digest[20], SHA1_CTX *context) 204 | { 205 | uint32_t i; 206 | uint8_t finalcount[8]; 207 | 208 | for(i = 0; i < 8; i++) 209 | { 210 | finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ 211 | } 212 | 213 | SHA1Update(context, (uint8_t *)"\200", 1); 214 | 215 | while((context->count[0] & 504) != 448) 216 | { 217 | SHA1Update(context, (uint8_t *)"\0", 1); 218 | } 219 | 220 | SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ 221 | 222 | for(i = 0; i < 20; i++) 223 | { 224 | digest[i] = (uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); 225 | } 226 | 227 | /* Wipe variables */ 228 | i = 0; 229 | memset(context->buffer, 0, 64); 230 | memset(context->state, 0, 20); 231 | memset(context->count, 0, 8); 232 | memset(&finalcount, 0, 8); 233 | } 234 | -------------------------------------------------------------------------------- /corelib/l1/sha1.h: -------------------------------------------------------------------------------- 1 | #ifndef _SHA1_H_ 2 | #define _SHA1_H_ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | extern void compute_hash_str(uint8_t *buffer, int bufferlen, uint8_t digest[20]); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | #endif // _SHA1_H_ 16 | -------------------------------------------------------------------------------- /kernel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "entrypoint.h" 3 | 4 | void RunTest(); 5 | 6 | int main(int argc, char **argv) 7 | { 8 | std::cout << "Kernel " << emvl2Version() << "\n"; 9 | RunTest(); 10 | } 11 | 12 | void RunTest() { 13 | emvl2Init("Gemplus USB Smart Card Reader 0", NULL, NULL); 14 | 15 | emvl2AddCaKey("A000000003", 0x94, "ACD2B12302EE644F3F835ABD1FC7A6F62CCE48FFEC622AA8EF062BEF6FB8BA8BC68BBF6AB5870EED579BC3973E121303D34841A796D6DCBC41DBF9E52C4609795C0CCF7EE86FA1D5CB041071ED2C51D2202F63F1156C58A92D38BC60BDF424E1776E2BC9648078A03B36FB554375FC53D57C73F5160EA59F3AFC5398EC7B67758D65C9BFF7828B6B82D4BE124A416AB7301914311EA462C19F771F31B3B57336000DFF732D3B83DE07052D730354D297BEC72871DCCF0E193F171ABA27EE464C6A97690943D59BDABB2A27EB71CEEBDAFA1176046478FD62FEC452D5CA393296530AA3F41927ADFE434A2DF2AE3054F8840657A26E0FC617", 16 | "03"); 17 | emvl2AddCaKey("A000000004", 0xEF, "A191CB87473F29349B5D60A88B3EAEE0973AA6F1A082F358D849FDDFF9C091F899EDA9792CAF09EF28F5D22404B88A2293EEBBC1949C43BEA4D60CFD879A1539544E09E0F09F60F065B2BF2A13ECC705F3D468B9D33AE77AD9D3F19CA40F23DCF5EB7C04DC8F69EBA565B1EBCB4686CD274785530FF6F6E9EE43AA43FDB02CE00DAEC15C7B8FD6A9B394BABA419D3F6DC85E16569BE8E76989688EFEA2DF22FF7D35C043338DEAA982A02B866DE5328519EBBCD6F03CDD686673847F84DB651AB86C28CF1462562C577B853564A290C8556D818531268D25CC98A4CC6A0BDFFFDA2DCCA3A94C998559E307FDDF915006D9A987B07DDAEB3B", 18 | "03"); 19 | 20 | emvl2AddAidPrms("00", "9F3501229F3303E0F0C89F4005FF00F0A0019F1A0207925F2A020949DF8B110400000000DF8B120B9F37049F47018F019F3201DF8B13039F0802DF8B140100DF8B150100DF812005F850ACA000DF8121050000000000DF812205F850ACF8009F090200029F1B0400000000"); 21 | emvl2AddAidPrms("A0000003330101", "9F09020020"); 22 | emvl2AddAidPrms("A0000000031010", "9F090200969F1B0400003A98"); 23 | emvl2AddAidPrms("A0000000032010", "9F0902008C9F1B04000000FA"); 24 | emvl2AddAidPrms("A0000000033010", "9F0902008C9F1B0400000000"); 25 | emvl2AddAidPrms("A0000000038010", "9F0902008C9F1B0400000000"); 26 | emvl2AddAidPrms("A0000000041010", ""); 27 | emvl2AddAidPrms("A0000000043060", ""); 28 | emvl2AddAidPrms("A0000000046000", ""); 29 | emvl2AddAidPrms("A0000000046010", ""); 30 | emvl2AddAidPrms("A0000000042203", ""); 31 | emvl2AddAidPrms("A00000002501", "9F090200019F1B0400003A98"); 32 | emvl2AddAidPrms("A0000003241010", ""); 33 | emvl2AddAidPrms("A0000001523010", "9F1B0400003A98"); 34 | emvl2AddAidPrms("A0000001524010", ""); 35 | emvl2AddAidPrms("A0000000651010", "9F090202009F1B04000007D0"); 36 | emvl2AddAidPrms("A0000006151010", ""); 37 | emvl2AddAidPrms("A0000005241010", ""); 38 | emvl2AddAidPrms("A0000002771010", "9F090200019F1A0201245F2A020124"); 39 | 40 | emvl2Start(0, 0, "100", "0"); 41 | 42 | uint8_t buffer[64]; 43 | int len = emvl2GetTag(0x95, buffer, sizeof(buffer)); 44 | if (len > 0) { 45 | printf("%02X%02X%02X%02X%02X\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /kernel.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33723.286 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kernel", "kernel.vcxproj", "{56DD377D-F53F-4931-BA0A-5CEE4A567998}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "corelib", "corelib\corelib.vcxproj", "{2C6CBD71-80F4-43BB-AF55-A5142DC165C2}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {56DD377D-F53F-4931-BA0A-5CEE4A567998}.Debug|x64.ActiveCfg = Debug|x64 19 | {56DD377D-F53F-4931-BA0A-5CEE4A567998}.Debug|x86.ActiveCfg = Debug|Win32 20 | {56DD377D-F53F-4931-BA0A-5CEE4A567998}.Debug|x86.Build.0 = Debug|Win32 21 | {56DD377D-F53F-4931-BA0A-5CEE4A567998}.Release|x64.ActiveCfg = Release|x64 22 | {56DD377D-F53F-4931-BA0A-5CEE4A567998}.Release|x64.Build.0 = Release|x64 23 | {56DD377D-F53F-4931-BA0A-5CEE4A567998}.Release|x86.ActiveCfg = Release|Win32 24 | {56DD377D-F53F-4931-BA0A-5CEE4A567998}.Release|x86.Build.0 = Release|Win32 25 | {2C6CBD71-80F4-43BB-AF55-A5142DC165C2}.Debug|x64.ActiveCfg = Debug|x64 26 | {2C6CBD71-80F4-43BB-AF55-A5142DC165C2}.Debug|x86.ActiveCfg = Debug|Win32 27 | {2C6CBD71-80F4-43BB-AF55-A5142DC165C2}.Debug|x86.Build.0 = Debug|Win32 28 | {2C6CBD71-80F4-43BB-AF55-A5142DC165C2}.Release|x64.ActiveCfg = Release|x64 29 | {2C6CBD71-80F4-43BB-AF55-A5142DC165C2}.Release|x64.Build.0 = Release|x64 30 | {2C6CBD71-80F4-43BB-AF55-A5142DC165C2}.Release|x86.ActiveCfg = Release|Win32 31 | {2C6CBD71-80F4-43BB-AF55-A5142DC165C2}.Release|x86.Build.0 = Release|Win32 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | GlobalSection(ExtensibilityGlobals) = postSolution 37 | SolutionGuid = {C7609ABD-0AB8-495A-A12D-5D7191F689C5} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /kernel.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 16.0 15 | Win32Proj 16 | {56dd377d-f53f-4931-ba0a-5cee4a567998} 17 | kernel 18 | 10.0 19 | 20 | 21 | 22 | Application 23 | true 24 | v143 25 | Unicode 26 | 27 | 28 | Application 29 | false 30 | v143 31 | true 32 | Unicode 33 | 34 | 35 | Application 36 | true 37 | v143 38 | Unicode 39 | 40 | 41 | Application 42 | false 43 | v143 44 | true 45 | Unicode 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Level3 68 | false 69 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 70 | true 71 | .\hal;.\corelib;%(AdditionalIncludeDirectories) 72 | stdcpp17 73 | 74 | 75 | Console 76 | true 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | Level3 86 | true 87 | true 88 | true 89 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | stdcpp17 92 | 93 | 94 | Console 95 | true 96 | true 97 | true 98 | 99 | 100 | copy /Y "$(TargetDir)$(TargetName).exe" "$(SolutionDir)..\kerneltool\lib\$(TargetName).exe" 101 | 102 | 103 | 104 | 105 | Level3 106 | true 107 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | true 119 | true 120 | true 121 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | {2c6cbd71-80f4-43bb-af55-a5142dc165c2} 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /kernel.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | --------------------------------------------------------------------------------