Marco Moschettini
Marco Moschettini

Reputation: 1735

Unsupported state or unable to authenticate data error when using NodeJS (with Typescript) streams and aes-gcm algorithm

When trying to encrypt and decrypt a file through a Cipher/Decipher stream I always encounter the following error: Unsupported state or unable to authenticate data.

This is the code (comparison between the stream and the not-stream versions):

Non stream version

const key = Buffer.alloc(256 / 8);

const text = 'my secret message';
const encrypter = crypto.createCipheriv('aes-256-gcm', key, Buffer.alloc(16));

let encrypted = encrypter.update(text, 'utf8', 'hex');
encrypted += encrypter.final('hex');
const tag = encrypter.getAuthTag();

console.log('Encrypted!', encrypted);

const decrypter = crypto.createDecipheriv('aes-256-gcm', key, Buffer.alloc(16));
decrypter.setAuthTag(tag);
let decrypted = decrypter.update(encrypted, 'hex', 'utf8');
decrypted += decrypter.final('utf8');

console.log('Decrypted', decrypted);

Which perfectly prints out Decrypted my secret message On the other hand...

Stream version

const key = Buffer.alloc(256 / 8);
const text = 'my secret message';

const cipher = crypto.createCipheriv('aes-256-gcm', key, Buffer.alloc(16));
let encrypted = '';
cipher.on('data', (data: Buffer) => 
{
    encrypted += data.toString('hex');
});

cipher.on('end', () => 
{
    const tag = cipher.getAuthTag();

    const decipher = crypto.createDecipheriv('aes-256-gcm', key, Buffer.alloc(16));
    decipher.setAuthTag(tag);

    let decrypted = '';
    decipher.on('readable', () => 
    {
        const data = decipher.read() as Buffer;
        if(data)
            decrypted += data.toString('utf8');
    });

    decipher.on('end', () => 
    {
        console.log(decrypted);
    });

    fromString(encrypted).pipe(decipher);
});

I'm assuming the utility function fromString picked from the examples of the from2 package

import * as from from 'from2';

function fromString(text: string) 
{
    return from((size, next) => 
    {
        if (text.length <= 0) 
            return next(null, null);

        const chunk = text.slice(0, size);
        text = text.slice(size);

        next(null, chunk);
    });
}

Any hint on why this is not working correctly? Thank you very much. I'm quite stuck on this.

Upvotes: 2

Views: 3099

Answers (1)

Marco Moschettini
Marco Moschettini

Reputation: 1735

Actually the following code is working fine on files. I don't know the differences between this and the one I posted about...

const algorithm = 'aes-256-gcm';
const iv = Buffer.alloc(16);
const key = Buffer.alloc(256/8);

const cipher = crypto.createCipheriv(algorithm, key, iv);
const read_stream = fs.createReadStream(path.resolve(os.homedir(), 'Desktop', 'abstract.pdf'));
const encrypted_write_stream = fs.createWriteStream(path.resolve(os.homedir(), 'Desktop', 'abstract.enc.pdf'));

cipher.on('finish', () => 
{
    const tag = cipher.getAuthTag();
    console.log('File encrypted. Tag is', tag.toString('hex'));

    const decipher = crypto.createDecipheriv(algorithm, key, iv);
    decipher.setAuthTag(tag);

    const encrypted_read_stream = fs.createReadStream(path.resolve(os.homedir(), 'Desktop', 'abstract.enc.pdf'));
    const write_stream = fs.createWriteStream(path.resolve(os.homedir(), 'Desktop', 'abstract.decrypted.pdf'));

    decipher.on('error', console.error);
    decipher.on('finish', () => 
    {
        console.log('Decrypted successfully');
    });

    encrypted_read_stream.pipe(decipher).pipe(write_stream);
});

read_stream.pipe(cipher).pipe(encrypted_write_stream);

Upvotes: 3

Related Questions