Debugging PubNub's encryption hole
Tags: reactnative, security
Published: 2021-09-13 08:34:43

**Introduction**
I work as a Lead Product Engineer with [MiniCorp](https://www.minicorphq.com/), 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](https://www.talkjs.com/) & [PubNub](https://www.pubnub.com/)

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](https://www.pubnub.com/docs/chat/features/encryption) to have built-in encryption support

Here's the screenshot of PubNub docs showing file encryption via the SDK
![Screenshot from PubNub docs at the time of writing](https://sriram-dev-images.s3.ap-south-1.amazonaws.com/pubnub_ss.png)

**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](https://www.pubnub.com/docs/sdks/javascript/react) 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.
![enter image description here](https://sriram-dev-images.s3.ap-south-1.amazonaws.com/pubnub_ss_0.JPG)

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](https://github.com/pubnub/javascript/search?q=supportsEncryptFile) of this variable name within the SDK repository. This threw an interesting finding that the variable was *true* for [web](https://github.com/pubnub/javascript/blob/master/src/file/modules/web.js#L25) & [node](https://github.com/pubnub/javascript/blob/master/src/file/modules/node.js#L26), but *false* for [react-native](https://github.com/pubnub/javascript/blob/787b95e8cff2a2fff4e32e7f512f0cd7aa7d7b4c/src/file/modules/react-native.js#L30)

    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. 
 ![PubNub support 1](https://sriram-dev-images.s3.ap-south-1.amazonaws.com/pubnub_ss_1.JPG)

![PubNub support 2](https://sriram-dev-images.s3.ap-south-1.amazonaws.com/pubnub_ss_2.JPG)

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.