We have a Python application that stores strings as encrypted binary data in MongoDB, it uses
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
On the NodeJS side I've been having trouble figuring out how to decrypt the data, I have our salt, our key, but as far as I can tell there's no IV, or the python module may just be hiding all of that under the hood as all the python application has to do is call encrypt(value, salt) and decrypt(value, salt)
Python:
class ChaChaEncryptedStringField(EncryptedStringField):
"""
A field which, given an encryption key and salt, will automatically encrypt/decrypt
sensitive data to avoid needing to do this before passing in. This encryption
method reliably produces a searchable string.
"""
def __init__(self, key, salt, *args, **kwargs):
"""Initialize the ChaChaEncryptedStringField.
Args:
key (str) -
salt (str) -
"""
class Hook:
def __init__(self, key, salt):
self.salt = salt
self.chacha = ChaCha20Poly1305(key)
def encrypt(self, value):
return self.chacha.encrypt(self.salt, value, None)
def decrypt(self, value):
return self.chacha.decrypt(self.salt, value, None)
self.encryption_hook = Hook(b64decode(key), b64decode(salt))
super(EncryptedStringField, self).__init__(*args, **kwargs)
Javascript (that isn't working but close):
const authTagLocation = data.buffer.length - 16;
const ivLocation = data.buffer.length - 28;
const authTag = data.buffer.slice(authTagLocation);
const iv = data.buffer.slice(ivLocation, authTagLocation);
const encrypted = data.buffer.slice(0, ivLocation);
const decipher = crypto.createDecipheriv('chacha20-poly1305', keyBuffer, iv,{ authTagLength: 16 } );
let dec = decipher.update(
data.buffer, 'utf-8', 'utf-8'
);
dec += decipher.final('utf-8');
return dec.toString();
With some research and trial and error I got past it complaining about improper IV, and key length is correct, but still getting garbled data back out
So I actually got the following code to work, but I'm not going to claim to fully understand what is happening:
working Javascript (salt is pulled from secrets, using the IV extracted fails)
const authTagLength = 16
const authTagLocation = data.buffer.length - authTagLength;
const ivLocation = data.buffer.length - 16;
const authTag = data.buffer.slice(authTagLocation);
const iv = data.buffer.slice(ivLocation, authTagLocation);
const encrypted = data.buffer.slice(0, ivLocation);
const decipher = crypto.createDecipheriv('chacha20-poly1305', keyBuffer, saltBuffer,{ authTagLength: authTagLength } );
let dec = decipher.update(
encrypted, 'utf-8', 'utf-8'
);
dec += decipher.final('utf-8');
return dec.toString();