Yes, we created a whole-new EMV kernel from scratch.
No, we haven't messed with our mind. No, we don't have to much time so we can waste it. It's simple. Our existing kernel no longer caters our needs. Frankly, there is a bunch of factors that made us give up on the existing kernel.
Let's talk about the real reasons and how we addressed them.
Architecture
We'll start with the most important part, the kernel architecture.
The first release can be called monolithic, since it appears as a solid C++98 project. Why C++98 instead of a fresher version, you ask? The answer is NewPOS 8210 whose SDK doesn't support anything newer than C++0x. And it's still too buggy.
Going back to the kernel architecture, it's worth mentioning that there were no division for different payment systems (MasterCard, Visa, Mir), which hampered error fixing and adding new functions. In fact, that was a monolithic chunk of legacy code that no one wanted to—and only few could—maintain.
When we needed to use the kernel on Android terminals, we had to implement Java support. That was quite a challenge.
Fortunately, the revamped architecture has no such problems. Developing and maintaining the kernel, we had enough time to understand that we need to upscale it and in which direction. It came to our minds immediately that we needed a modular architecture—where each components can be easily replaced.
We had also been speculating whether to use Lua as another programming language that can be efficient as an embeddable language with very low overhead.
Graphically, the architecture can be represented in the figure below.
EMV Kernel Representation.
Direct your attention to the lowest module, «Payment kernels» Written fully in Lua, it is completely replaceable. To support this feature, we had to build a Lua interpreter into the kernel.
It helped us shrink the implementation-testing workflow since now there is no kernel rebuilding stage. We only have to replace a Lua file of the payment kernel and continue testing.
Some drawbacks also popped. A trade-off was longer application execution time, since there were code interpretation overhead. But this toll is so insignificant that the client won't notice it.
Let's go further and look at the «Client application» module that is called to maintain the card reader and fulfill a bunch of cryptographic functions used in the kernel. All this was done to make the kernel seamlessly portable between platforms.
Here we approach the main module—the middle layer that implements the basic parts of the kernel, namely:
- Entrypoint. Main entry point.
- Lib. Auxiliary libraries (log, tlv, emv, etc.).
- Lua. Lua interpreter.
This level builds a bridge between the client application and Lua-based kernels. I would call this layer «the core of the kernel». But it sounds too tautological, so we stick with «Kernel base».
Now we can move on to the technological basis of the project.
Technology stack
This is the most geeky part. We will unveil our technology stack and tell a little about our pipeline.
While C++ remains our main language, we already use the C++11 standard. Our second language, as you might already guess, is Lua version 5.4.4 (the latest version to date).
To build the project, we use CMake with a 3.12 minimum version limit. We could take meson with ninja but we need smooth kernel build in Android Studio where CMake runs flawlessly.
Last but not least, is logging library Plog.
This is where our programming stack ends and integration with Gitlab CI starts. Today, most of routine tasks go to CI. For this project, only three simple stages were made.
Gitlab CI/CD stages.
The first is about building the project. It's all simple. We select tools in the container and use CMake to assemble.
Next comes the testing stage where tests are run automatically and the resulting artifacts are sent to Gitlab CI so we can review the output in the web interface.
The last stage is generating documentation that is automatically uploaded to the project Wiki. This completes project automation in Gitlab.
I also want to mention how we maintain kernel building for Android. This feature is implemented in a standalone repository to which this project is connected as a submodule. Apart from that, it's a normal Java binding over C++ code with all the features.
I'd also like to boast of another feature that is very easy to implement in the project. I mean loading and hot swapping of the payment core. Since payment cores are built on Lua, it's easy to load them over the network and adapt the kernel to them, all on the go. This feature might look simple. But just think that you can make changes and deliver them to end-user applications without introducing an update. Isn't it cool?