Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Boring Crypto Incompatible with Node.js #68

Closed
timsweb opened this issue Mar 30, 2022 · 2 comments
Closed

Boring Crypto Incompatible with Node.js #68

timsweb opened this issue Mar 30, 2022 · 2 comments

Comments

@timsweb
Copy link

timsweb commented Mar 30, 2022

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

@paragonie-security
Copy link
Contributor

This is an issue with the JS implementation; the PHP implementation is canonical. We'll look into it.

@paragonie-security
Copy link
Contributor

Fixed in paragonie/ciphersweet-js@320f762

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants