With the conclusion of Google Summer of Code 2024, we’re thrilled to share that four of our sponsored projects achieved remarkable success!
Continuing our series of posts highlighting each project, today we’re featuring the work of @wh201906. This project tackled the challenge of optimising encryption and decryption processes, boosting speed for mobile devices in particular (but desktop too). Additionally, wh201906 improved the encryption security by transitioning to a more robust 256-bit key.
Stay tuned as we dive into the specifics of each project in upcoming posts, and join us in celebrating the achievements of our GSoC 2024 contributors.
Contributor: @wh201906
As a Joplin user, I tried to use it for storing my study notes, but I noticed it is very slow when attaching large files to the notes, so I ended using it to save all my studying materials. However, when I found the Native Encryption in Joplin’s GSoC 2024 project ideas, I thought it was a good opportunity for me to fulfill my requirements on my own.
The old encryption implementation is based on the Stanford JavaScript Cryptography Library (sjcl), which is a pure JS library and is slow. To resolve the performance issue, I need to either find some native encryption libraries or write the encryption logic with the platform-specific codes (C/C++/Java/Kotlin/Objective-C/Swift). I did some evaluations before the coding period and selected node:crypto
for the desktop clients because it is mature and powered by OpenSSL, but I didn’t find a native encryption library working out of the box for the mobile platform, so I planned to write a React Native encryption library with javax.crypto
and OpenSSL
as the low-level implementation. Thankfully, I found react-native-quick-crypto
matches my need and this saved a lot of time, which I can use for improving the encryption methods.
It's interesting that during the GSoC period, @personalizedrefriger came out an idea to use the Web Crypto API
for the desktop clients and web clients, and it turned out to be working. The Web Crypto API
has similar interface with the node:crypto
, so we don't need to change too much code switching the libraries. In the end, the Web Crypto API
shows a similar performance as the node:crypto
in Node.js, which is reasonable because I guess they both use the OpenSSL as the backend, and it makes it possible to use the new encryption methods in the experimental web clients. Thanks @personalizedrefriger for the migration process!
I also want to improve the encryption methods out of security. We once tried to switch from AES-128 to AES-256 for the note content encryption in 2023 but met noticeable performance regressions, but this won’t be a problem for the new implementation. I also increased the PBKDF2 key iteration count from 10000 to 220000, to match the OWASP Password Storage Cheat Sheet. There are a few more improvements for improving the security, and you can find it in the final report on the forum.
It's also noticeable that I switched the cipher mode from AES-CCM to AES-GCM, which is more widely used (and should be better than AES-CCM) but might have vulnerabilities if the key-IV pair is reused. In the old implementation, the IV is generated randomly and could collide in a low possibility as the Birthday Problem describes. To mitigate this, I increased the salt length of generating content encryption key, and rotate both the salt and the IV for each encryption. Then I got a (256 + 96) bits equivalent nonce. I also introduced random bytes, timestamp and a counter in the nonce to make each nonce more unique. The timestamp and counter parts are also hashed with random bytes so the nonce is not predictable.
These improvements didn't come in one go. During the coding period, I kept on reading related passages about the ciphers and communicated with reviewers for the suggestions, which brought me new ideas to gradually improve the code overtime. As the result, the PR involves over 71 commits with the changes over the PR target, the code, the library, the nonce generation method and the encryption parameters. The first commit was just a basic framework, but after many iterations and discussions, the PR evolved into a complete implementation with tests.
Now the new methods have higher performance. The encrypted files and encrypted master key is smaller, and encryption speeds are significantly improved, with up to 8x faster performance on mobile platforms and up to 16x faster on desktop platforms. None of these improvements sacrifice security, and the new methods are actually more secure. Anyone can review the design by checking the codebase and my final report.
I would like to thank @tessus and, @roman_r_m, @laurent and @personalizedrefriger for their support. Before GSoC, I have only contributed to Joplin without a solid understanding of TypeScript and I was just trying to make all kinds of patches to small pieces of code. This is my first time to write a new feature in Typescript with considerable amount of code, and I was even nervous about if I am competent for this job before the GSoC starts. Thankfully, they supported me by reviewing my code and making suggestions over and over again. I can feel that you put extra effort in reviewing my GSoC PR than my previous PRs, and this helped me learn Typescript way much faster. Besides that, you also taught me some good coding practice and some ideas in project management, which were precious and I wouldn't have learned them if I were just coding on my own. I also once run into some troubles for getting a macOS development environment, and they keep helping me for this in every way. I'm so grateful for the experience of contributing to Joplin during GSoC.