Boring Crypto Incompatible with Node.js
timsweb opened this issue · 2 comments
It's not possible to encrypt/decrypt between the PHP library (v3.0.5) and Node library (v2.0.5). I believe this is because they treat AAD differently. In node the AAD is concatenated to the nonce when calculating the MAC. In PHP the AAD is not concatenated.
In node:
if (aad.length >= 0) {
if (!Buffer.isBuffer(aad)) {
aad = await Util.toBuffer(aad);
}
aad = Buffer.concat([nonce, aad]);
} else {
aad = nonce;
}
In PHP:
if (is_null($aad)) {
$aad = '';
}
I'm not sure if this is the only source of incompatibility, but as it stands I can't find a way to have portability between PHP and Node. I think care would be needed in any change around this to prevent breaking decrypting of any stored values. Perhaps the behaviour should be configurable.
Edit: I tried making the behaviour configurable and that got me past the MAC check, but decrypting in node a value encrypted from PHP resulted in corrupt data. I believe the inputs to the xchacha20 functions are the same in both languages, but the output I'm getting is different.
In Node:
const xchacha = new XChaCha20();
const decrypted = await xchacha.decrypt(
encrypted,
nonce,
(await this.getEncryptionKey(encKey)).getRawKey()
);
await sodium.sodium_memzero(encKey);
return decrypted;
in PHP:
return \sodium_crypto_stream_xchacha20_xor(
$encrypted,
$nonce,
$this->getEncryptionKey($key)->getRawKey()
);
To test this hypothesis I've thrown together a quick test case:
echo \sodium_crypto_stream_xchacha20_xor(
hex2bin('549cad2cd9bc64'),
hex2bin('9e985dfdfdf85321b3171596a213fd3e819f0d9800fce29f'),
hex2bin('82a9032e85e88ac6ed8365b8f3280d9ff9d8a88728886d18d9b52dd0fc6c5da1')
);
//outputs "qwertyu"
const { ChaCha20, HChaCha20, XChaCha20 } = require('xchacha20-js');
const xchacha = new XChaCha20();
xchacha.decrypt(
Buffer.from('549cad2cd9bc64', 'hex'),
Buffer.from('9e985dfdfdf85321b3171596a213fd3e819f0d9800fce29f', 'hex'),
Buffer.from('82a9032e85e88ac6ed8365b8f3280d9ff9d8a88728886d18d9b52dd0fc6c5da1', 'hex')
).then(b => console.log(b.toString('utf-8')));
//outputs ")���<\"
Thanks
Tim
This is an issue with the JS implementation; the PHP implementation is canonical. We'll look into it.
Fixed in paragonie/ciphersweet-js@320f762