Introduction
I work as a Lead Product Engineer with MiniCorp, where we work towards bringing our customer's ideas to life. In the recent past we have seen a surge in the number of customers requiring in-app chat within their product.
In-App Chat
This is the age of SaaS / API, where there are ton of options for each niche. These products provide a complete package which would take months for us to build from scratch. In our search for In-app chat, 2 products stood out the most. TalkJS & PubNub
We use both these products depending on the client's requirements. While TalkJS doesn't provide a free version, PubNub offers a limited free version. While in the long run TalkJS's price model is easy to maintain, for clients who prefer to launch their MVP, PubNub's free version makes more sense.
PubNub's Encryption Claim
One of our recent clients had the requirement that all chat messages & attachments should be end-to-end encrypted and no unencrypted data should be stored on the server. We went ahead and started building the product with PubNub as they claim to have built-in encryption support
Here's the screenshot of PubNub docs showing file encryption via the SDK
Implementation
We implemented the chat on our Ruby on Rails app with Javascript SDK. Everything worked as expected and we kick started our next phase of development which was building react native app.
PubNub provides a neat react SDK which is a wrapper on their Javascript SDK. As usual we were testing our implementation with plain text messages and everything worked as expected. The entire chat functionality was built & everyone was happy until we noticed a key detail.
The files uploaded were stored on PubNub's servers and we were assuming that all files were encrypted. To our rude shock, when we used the getFileUrl
method and opened the link on a tab, we saw the unencrypted version of the file (hosted on PubNub's S3 account)
Analysis
We were so sure that we did some mistake, because files uploaded through the web version were getting encrypted properly. We were unsuccessful in our attempts and contacted the PubNub support. The support assured us that other clients were infact using the SDK and encryption was working for them.
Since we couldn't find any issue with our code, I decided to dig into PubNub's SDK source code.
1. Digging into sendFile method
We started with the public method that was mentioned in the document.
https://github.com/pubnub/javascript/blob/master/src/core/endpoints/file_upload/send_file.js#L64
if (PubNubFile.supportsEncryptFile && (cipherKey ?? config.cipherKey)) {
file = await cryptography.encryptFile(cipherKey ?? config.cipherKey, file, PubNubFile);
}
The 1st clue was looking at the PubNubFile.supportsEncryptFile
boolean. It was a surprise seeing such a variable.
2. supportsEncryptFile
boolean
I did a quick search of this variable name within the SDK repository. This threw an interesting finding that the variable was true for web & node, but false for react-native
static supportsEncryptFile = false;
3. Contacting support
With this interesting finding, I reached out to PubNub support. And the response was that I needed to encrypt the file with the JS utility method before using the sendFile method.
This was unexpected & nowhere mentioned in their docs. As we saw in the above snippet, sendFile
method internally calls the encryptFile
method if cipherText
is provided.
4. Implementing encryptFile
Inspite of the above, I decided to give it a try. When I called the utility method directly, I ended up getting the following error
TypeError: undefined is not an object (evaluating 'cryptography.encryptFile')
5. Understanding the encryptFile
method
While I assumed that manually encrypting the file would work, this was another surprise to see that the method could not be found. So I decided to dig deeper into the encryptFile
method.
https://github.com/pubnub/javascript/blob/master/src/core/pubnub-common.js#L379
this.encryptFile = (key, file) => cryptography.encryptFile(key, file, this.File);
https://github.com/pubnub/javascript/blob/master/src/core/pubnub-common.js#L358
const cryptography = setup.cryptography;
Here I noticed that the encryptFile method is a nice interface wrapper on the cryptography module. The cryptography module itself is an attribute to the setup, which is the constructor arugument.
cryptography
variable is initialized on each module constructor depending on the platform
Web
https://github.com/pubnub/javascript/blob/master/src/web/index.js#L101
setup.cryptography = new WebCryptography();
Node
https://github.com/pubnub/javascript/blob/master/src/node/index.js#L23
setup.cryptography = new NodeCryptography();
And, this was not initialized on the react-native module
https://github.com/pubnub/javascript/blob/88027bb862608796a6648f175e88c6b7bb6030f3/src/react_native/index.js
Conclusion
As I mentioned before, working with 3rd party tools really fast tracks the development time & enables us to go-to-market sooner than if we built everything. But the trade-off is not having control when something doesn't work as expected.
Luckily PubNub has an open source repository which allowed us to dive deeper into this issue. If we had known the lack of encryption support for react native SDK, we could have taken a different call.
We have written to PubNub's support with all our above findings and hoping for a fix soon. I will update the post as & when I hear more updates from PubNub team.
Top comments (0)