Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AES 256 GCM encryption decryption in nodejs

I am implementing a basic encryption/decryption set of functions in nodejs and I keep getting the following error in the decryption part:
Error: Unsupported state or unable to authenticate data

This is my code so far:

import crypto from 'crypto'
import logger from './logger'

const ALGORITHM = 'aes-256-gcm'

export const encrypt = (keyBuffer, dataBuffer, aadBuffer) => {
  // iv stands for "initialization vector"
  const iv = Buffer.from(crypto.randomBytes(12), 'utf8')
  logger.debug('iv: ', iv)
  const encryptor = crypto.createCipheriv(ALGORITHM, keyBuffer, iv)
  logger.debug('encryptor: ', encryptor)
  logger.debug('dataBuffer: ', dataBuffer)
  return Buffer.concat([iv, encryptor.update(dataBuffer, 'utf8'), encryptor.final()])
}

export const decrypt = (keyBuffer, dataBuffer, aadBuffer) => {
  const iv = dataBuffer.slice(0, 96)

  const decryptor = crypto.createDecipheriv(ALGORITHM, keyBuffer, iv)
  return Buffer.concat([decryptor.update(dataBuffer.slice(96), 'utf8'), decryptor.final()])
}

My error happens in the last line of the decrypt function. I am storing the iv as part of the dataBuffer.

Thanks in advance!

like image 648
vanvasquez Avatar asked Nov 12 '18 19:11

vanvasquez


People also ask

Can AES 256 be decrypted?

Only those who have the special key can decrypt it. AES uses symmetric key encryption, which involves the use of only one secret key to cipher and decipher information.

Can you decrypt AES 256 without key?

In reality the 256-bit AES is considered computationally infeasible. The only 'feasible' way to decrypt would be using the same key used to encrypt.

Can 256 AES be cracked?

AES 256 is virtually impenetrable using brute-force methods. While a 56-bit DES key can be cracked in less than a day, AES would take billions of years to break using current computing technology. Hackers would be foolish to even attempt this type of attack. Nevertheless, no encryption system is entirely secure.


1 Answers

I realized I had made a couple of mistakes with the original code that I posted, one of them as @TheGreatContini remarked was the size of the slicing which was being done in bits instead of bytes as it should be. Still, the biggest piece that I was missing was the authTag which always should be included in the decipher function setup.

Here is my working code for anybody interested for future references:

import crypto from 'crypto'
import logger from './logger'

const ALGORITHM = 'aes-256-gcm'

export const encrypt = (keyBuffer, dataBuffer, aadBuffer) => {
  // iv stands for "initialization vector"
  const iv = crypto.randomBytes(12)
  const cipher = crypto.createCipheriv(ALGORITHM, keyBuffer, iv)
  const encryptedBuffer = Buffer.concat([cipher.update(dataBuffer), cipher.final()])
  const authTag = cipher.getAuthTag()
  let bufferLength = Buffer.alloc(1)
  bufferLength.writeUInt8(iv.length, 0)
  return Buffer.concat([bufferLength, iv, authTag, encryptedBuffer])
}

export const decrypt = (keyBuffer, dataBuffer, aadBuffer) => {
  const ivSize = dataBuffer.readUInt8(0)
  const iv = dataBuffer.slice(1, ivSize + 1)
  // The authTag is by default 16 bytes in AES-GCM
  const authTag = dataBuffer.slice(ivSize + 1, ivSize + 17)
  const decipher = crypto.createDecipheriv(ALGORITHM, keyBuffer, iv)
  decipher.setAuthTag(authTag)
  return Buffer.concat([decipher.update(dataBuffer.slice(ivSize + 17)), decipher.final()])
}
like image 144
vanvasquez Avatar answered Sep 21 '22 07:09

vanvasquez