This morning I started working on a feature for https://nazcabot.io, which requires some data to be securely encrypted in Node.js and eventually passed to a Python process, decrypted and used.

Note that we’re talking about symmetric secret-key authenticated encryption. You have some data and you want to encrypt it using a secret key.

“Eaaasy peaaasy” you might be thinking, as I did too. Nope. Nope nope nope.

The obvious solution is to use Node’s crypto library, which is built on top of OpenSSL. Now, the most popular Python crypto package, PyCrypto is not. Although the underlying encryption algorithm is the same (in my case aes-256-cbc ), the way OpenSSL pads your data (PKCS#5) is different from what PyCrypto does, causing unnecessary headaches.

Photo by Mike Szczepanski on Unsplash

Enter NaCl or TweetNaCl or libsodium

To avoid incompatibility issues, you can use libsodium, which is a modern, portable, easy to use crypto library written in C with wrappers for most languages. In particular, it has wrappers for Node.js and Python.

Encryption in Node.js

I used a library called tweenacl-js and a sibling library, tweetnacl-util, which comes with a few handy dandy functions, you can install it with:

npm install --save tweetnacl tweetnacl-util

Now lets dive in. Note that I have hardcoded the secret key. Don’t do that. Put the key in an .env file and load it up with dotenv.

const nacl = require('tweetnacl')

const utils = require('tweetnacl-util')

const encodeBase64 = utils.encodeBase64 // Our nonce must be a 24 bytes Buffer (or Uint8Array)

const nonce = nacl.randomBytes(24) // Our secret key must be a 32 bytes Buffer (or Uint8Array)

const secretKey = Buffer.from('_THIS_IS_MY_32_CHARS_SECRET_KEY_', 'utf8') // Make sure your data is also a Buffer of Uint8Array

const secretData = Buffer.from('Some Italians hate wine', 'utf8')

const encrypted = nacl.secretbox(secretData, nonce, secretKey) // We can now store our encrypted result and our nonce somewhere

const result = `${encodeBase64(nonce)}:${encodeBase64(encrypted)}`

result will look something like this

6mNohLkeVCPgv6r4Jfx2cRhFHtnIa04K:rWTEbQ0GdzpXdxXZ9JRk+drr3JtEmt1I70DGNpXvPO9lKgOZbflf

With a : dividing out nonce from our encrypted data. It’s important that the nonce is different every time, hence we generate it randomly and save it for later.

Decryption is Python

Install pynacl with pip or whatever you prefer.

pip install pynacl

Now, assume you already have the string from above, you can decrypt it in a couple of lines

from base64 import b64decode

from nacl.secret import SecretBox secret_key = '_THIS_IS_MY_32_CHARS_SECRET_KEY_'

encrypted = '6mNohLkeVCPgv6r4Jfx2cRhFHtnIa04K:rWTEbQ0GdzpXdxXZ9JRk+drr3JtEmt1I70DGNpXvPO9lKgOZbflf' encrypted = encrypted.split(':') # We decode the two bits independently

nonce = b64decode(encrypted[0])

encrypted = b64decode(encrypted[1]) # We create a SecretBox, making sure that out secret_key is in bytes

box = SecretBox(bytes(secret_key, encoding='utf8')) decrypted = box.decrypt(encrypted, nonce).decode('utf-8')

And there you have it

'Some Italians hate wine'

Again, make sure your secret key is not stored in plain text in your code, please.