Can the value from node crypto.createCipheriv('aes-256-gcm', ...).getAuthKey() be public?

1.7k Views Asked by At

I'm having trouble finding some information. Does anyone know if the value returned from cipher.getAuthTag() (--> returns MAC) can be publicly visible?

TL;DR

Can a message authentication code be publicly visible, or does this need to be kept secret like a password?

Some background, I am trying to encrypt a file. I found this stackoverflow question and answer that helped me get started. https://stackoverflow.com/a/27345933/11070228

After doing some research in the nodejs documentation, I found that the answer uses a deprecated function. createCipher. The new function to use should be createCipheriv.

So, to use the new createCipheriv, I used the documentation to write a new encryption and decryption function, similar to the one in the post using the new createCipheriv function. After writing the decryption function, I got an error that was

Error: Unsupported state or unable to authenticate data

After googling that issue, it led me here to this github post. In a nutshell, it said that the authTag generated with the cipher is needed to decrypt the file.

I did not know what this authTag was, and neither did anyone I knew. So I started googling that and it let me to this blogpost. It states

The authTag is the message authentication code (MAC) calculated during the encryption.

And here is a wikipedia article on what a message authentication code is.

So. Here is my question. Can a message authentication code be publicly visible, or does this need to be kept secret like a password?


My code, not as relevant, but might help someone create the encryption and decryption using createCipheriv and createDecipheriv.

Encryption

const crypto = require('crypto');
const fs = require('fs');

// const iv = crypto.randomBytes(32).toString('hex');
// EDIT - based on @President James K. Polk. The initialization vector should be 12 bytes long
// const iv = crypto.randomBytes(6).toString('hex');
// EDIT - based on @dsprenkels. I misunderstood @President James K. Polk
const iv = crypto.randomBytes(12).toString('hex');
const privateKey = 'private key that is 32 byte long';
const cipher = crypto.createCipheriv('aes-256-gcm', privateKey, iv);

const filename = 'somefile.txt';
const encFilename = 'somefile.txt.enc';
const unencryptedInput = fs.createReadStream(filename);
const encryptedOutput = fs.createWriteStream(encFilename);
unencryptedInput.pipe(cipher).pipe(encryptedOutput);

encryptedOutput.on('finish', () => {
    const authTagAsHex = cipher.getAuthTag().toString('hex'); // <-- can this be public
    console.log(authTagAsHex);
});

Decryption

const crypto = require('crypto');
const fs = require('fs');

// const publicIV = 'same iv generated during encryption crypto.randomBytes(32).toString("hex")';
// EDIT - based on @President James K. Polk. The initialization vector should be 12 bytes long
// const publicIV = 'same iv generated during encryption crypto.randomBytes(6).toString("hex")';
// EDIT - based on @dsprenkels. I misunderstood @President James K. Polk
const publicIV = 'same iv generated during encryption crypto.randomBytes(12).toString("hex")';
const authTag = 'same authKey generated from cipher.getAuthTag().toString("hex")';
const privateKey = 'private key that is 32 byte long';
const decipher = crypto.createDecipheriv('aes-256-gcm', privateKey, publicIV);
decipher.setAuthTag(Buffer.from(authTag, 'hex'));

const filename = 'somefile.txt';
const encFilename = 'somefile.txt.enc';
const readStream = fs.createReadStream(encFilename);
const writeStream = fs.createWriteStream(filename);
readStream.pipe(decipher).pipe(writeStream);
1

There are 1 best solutions below

1
On BEST ANSWER

Yes. The MAC is considered public.

In general, message authentication codes are considered public. A message authentication code authenticates the (encrypted) message under the key that you provided. On other words, it is used by the receiver to check if the ciphertext did not change during transmission. In your situation, as long as the key remains secret, the attacker does not have any use for the MAC.

The MAC is normally put next to the ciphertext when the ciphertext is stored (just as the IV).


By the way, in your case you are were randomly generating the IV. That is fine, but beware that the amount of messages that can be safely encrypted under the same key is quite small. If an IV is used for multiple message (even once!) the complete security of this scheme breaks down. Really, you probably want this:

const iv = crypto.randomBytes(12);