├── .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