I am using Ruby's Open SSL bindings to do AES-256 encryption. I can encrypt a non-empty string. However, when attempting to encrypt an empty string, Ruby raises an exception complaining that the data must not be empty. How can I encrypt an empty string using Ruby's OpenSSL bindings?
require "openssl"
KEY = OpenSSL::Cipher::Cipher.new("aes-256-cbc").random_key
def encrypt(plaintext)
  cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
  cipher.encrypt
  iv = cipher.random_iv
  cipher.iv = iv
  cipher.key = KEY
  ciphertext = cipher.update(plaintext)    # <- ArgumentError here
  ciphertext << cipher.final
  [iv, ciphertext]
end
def decrypt(iv, ciphertext)
  cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
  cipher.decrypt
  cipher.iv = iv
  cipher.key = KEY
  plaintext = cipher.update(ciphertext)
  plaintext << cipher.final
  plaintext
end
p decrypt(*encrypt("foo"))    # "foo"
p decrypt(*encrypt(""))
# /tmp/foo.rb:11:in `update': data must not be empty (ArgumentError)
#         from /tmp/foo.rb:11:in `encrypt'
#         from /tmp/foo.rb:27:in `<main>'
I am writing an ETL program to migrate data from one database to a SqlServer database. Certain columns from the source database must be encrypted before writing them to the destination database. The source columns may contain any data, including empty strings. The destination columns are usually non-nullable. The destination columns will be decrypted by .net code.
Goal #1: No information about the encrypted field, including whether or not it even exists, should be recoverable without properly decrypting it. An encrypted empty string should be indistinguishable from any other encrypted data.
Goal #2: The .net code that will decrypt these values should not need to handle empty strings specially.
If I can get openssl to encrypt empty strings, I will achieve both of these goals.
I could just not encrypt empty strings, passing them through.
def encrypt(plaintext)
  return plaintext if plaintext.empty?
  ...
end
def decrypt(iv, ciphertext)
  return ciphertext if ciphertext.empty?
  ...
end
This has the disadvantages of exposing information, and also of requiring cooperating code to be written on the .net side.
I could add some constant string to the plaintext before encryption, and remove it after decryption:
PLAINTEXT_SUFFIX = " "
def encrypt(plaintext)
  plaintext += PLAINTEXT_SUFFIX
  ...
end
def decrypt(iv, ciphertext)
  ...
  plaintext.chomp(PLAINTEXT_SUFFIX)
end
This hides whether the data exists or not, but still requires cooperating .net code.
As suggested by @ArtjomB, it's as simple as not calling Cipher#update with the empty string.  The value returned by Cipher#final then properly encrypts an empty string.
require "openssl"
KEY = OpenSSL::Cipher::Cipher.new("aes-256-cbc").random_key
def encrypt(plaintext)
  cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
  cipher.encrypt
  iv = cipher.random_iv
  cipher.iv = iv
  cipher.key = KEY
  ciphertext = ""
  ciphertext << cipher.update(plaintext) unless plaintext.empty?
  ciphertext << cipher.final
  [iv, ciphertext]
end
def decrypt(iv, ciphertext)
  cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
  cipher.decrypt
  cipher.iv = iv
  cipher.key = KEY
  plaintext = cipher.update(ciphertext)
  plaintext << cipher.final
end
p decrypt(*encrypt("foo"))    # "foo"
p decrypt(*encrypt(""))       # ""
                        If you can use DBMS provided encryption functions, then, MySQL AES_ENCRYPT, seems to be able to encrypt blank string.
For example:
UPDATE some_table 
   SET some_column =  AES_ENCRYPT('',UNHEX('F3229A0B371ED2D9441B830D21A390C3'));
It's AES-128 by default, I am guessing that will be a problem as you need AES-256. Also, not sure which DBMS you are using and whether that DBMS has encryption functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With