Search code examples
rubyaes-gcm

Unable to decrypt encrypted message using another instance of cipher


I am using ruby version 2.4.0 and openssl version "OpenSSL 1.0.1f 6 Jan 2014", I am trying to implement an encryption/decryption for a security layer.

The code works fine if I write it as follows using the same cipher object.

# Example 1

require 'openssl'
require 'base64'

data = "Hello1"

cipher = OpenSSL::Cipher::AES128.new :GCM
cipher.encrypt
iv  = cipher.random_iv # random iv
key = cipher.random_key # 128 byte key
cipher.key = key
cipher.iv = iv
enc_data  = cipher.update(data) + cipher.final

cipher.decrypt
cipher.key = key
cipher.iv = iv
original_data = cipher.update(enc_data) + cipher.final

if data == original_data
  puts "Yes"
end

But in the second example where I have instantiated a second cipher object for decrypting I am getting a CipherError.

# Example 1

require 'openssl'
require 'base64'

def encrypt(data)
  cipher = OpenSSL::Cipher::AES128.new :GCM
  cipher.encrypt
  key = cipher.random_key
  iv  = cipher.random_iv
  cipher.key = key
  cipher.iv = iv
  enc_data = cipher.update(data) + cipher.final
  return enc_data, key, iv
end

def decrypt(data, key, iv)
  cipher = OpenSSL::Cipher::AES128.new :GCM
  cipher.decrypt
  cipher.key = key
  cipher.iv  = iv
  cipher.update(data) + cipher.final
end

data = 'Hello2'
enc_data, key, iv = encrypt(data)
original_data = decrypt(enc_data, key, iv)

if data == original_data
  puts "Yes"
end


OpenSSL::Cipher::CipherError:
from (irb):93:in `final'
from (irb):93:in `decrypt'
from (irb):98

Solution

  • GCM mode requires a bit more setup, compared to, say, CBC mode.

    Documentation: https://ruby-doc.org/stdlib-2.4.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html#class-OpenSSL::Cipher-label-Authenticated+Encryption+and+Associated+Data+-28AEAD-29

    An example using the GCM (Galois/Counter Mode). You have 16 bytes key, 12 bytes (96 bits) nonce and the associated data auth_data. ... Now you are the receiver. You know the key and have received nonce, auth_data, encrypted and tag through an untrusted network.

    Here's the updated code:

    require 'openssl'
    require 'base64'
    
    def encrypt(data)
      cipher = OpenSSL::Cipher::AES128.new(:GCM).encrypt
      key = cipher.random_key
      iv  = cipher.random_iv
      cipher.key = key
      cipher.iv = iv
      cipher.auth_data = ''
      enc_data = cipher.update(data) + cipher.final
      return enc_data, key, iv, cipher.auth_tag
    end
    
    def decrypt(data, key, iv, auth_tag)
      cipher = OpenSSL::Cipher::AES128.new(:GCM).decrypt
      cipher.decrypt
      cipher.key = key
      cipher.iv  = iv
      cipher.auth_data = ''
      cipher.auth_tag = auth_tag
      cipher.update(data) + cipher.final
    end
    
    data = 'Hello2'
    enc_data, key, iv, auth_tag = encrypt(data)
    original_data = decrypt(enc_data, key, iv, auth_tag)
    
    if data == original_data
      puts "Yes"
    end