Encrypt empty string

I am using Ruby Open SSL bindings for AES-256 encryption. I can encrypt a non-empty string. However, when trying to encrypt an empty string, Ruby throws an exception complaining that the data should not be empty. How can I encrypt an empty string using Ruby OpenSSL bindings?

Code to reproduce the problem

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>' 

Version

  • ruby 2.2.2p95
  • OpenSSL :: VERSION - "1.1.0"
  • Microsoft SQL Server 2014 (12.0.2000.8)

Why do I want to encrypt blank lines?

I am writing an ETL program to transfer data from one database to a SqlServer database. Some columns from the source database must be encrypted before being written to the target database. Source columns can contain any data, including empty rows. Destination columns are usually not NULL. Destination columns will be decrypted with .net code.

Goal No. 1: no information about the encrypted field, including that exists, should be restored without proper decryption. The encrypted empty string must be indistinguishable from any other encrypted data.

Goal # 2: The .net code that decrypts these values ​​should not process blank lines specifically.

If I can get openssl to encrypt empty strings, I will achieve both of these goals.

Workaround - do not encrypt blank lines

I could simply not encrypt blank lines by passing them.

 def encrypt(plaintext) return plaintext if plaintext.empty? ... end def decrypt(iv, ciphertext) return ciphertext if ciphertext.empty? ... end 

This has drawbacks in disclosing information, as well as the need to write collaboration code on the .net side.

Workaround - Add a constant to plaintext

I can add some constant to plaintext before encryption and delete it after decryption:

 PLAINTEXT_SUFFIX = " " def encrypt(plaintext) plaintext += PLAINTEXT_SUFFIX ... end def decrypt(iv, ciphertext) ... plaintext.chomp(PLAINTEXT_SUFFIX) end 

This hides whether or not the data exists, but still requires .net code collaboration.

+6
source share
2 answers

As @ArtjomB suggested , it is as simple as not invoking Cipher#update with an empty string. The value returned by Cipher#final then correctly encrypts the 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("")) # "" 
+7
source

If you can use the encryption functions provided by the DBMS, then MySQL AES_ENCRYPT seems to be able to encrypt an empty string.

For instance:

 UPDATE some_table SET some_column = AES_ENCRYPT('',UNHEX('F3229A0B371ED2D9441B830D21A390C3')); 

This is AES-128 by default, I assume this will be a problem since you need AES-256. In addition, you do not know which DBMS you are using and whether this DBMS has encryption functions.

+2
source

All Articles