I used a pretty standard example (which is severely broken for my purposes) of cbc encryption in ruby:
def aes(m,k,t) (aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(m)).key = Digest::SHA256.digest(k) aes.update(t) << aes.final end def encrypt(key, text) aes(:encrypt, key, text) end def decrypt(key, text) aes(:decrypt, key, text) end
This works as an acceptable starting point, but I need to be able to encrypt large streams of data without loading them into one huge chunk of memory. I want to download mega-at a time, update the state of the encryption stream, and then move on to the next block. Looking at the OpenSSL Cipher docs (which are award-winning), I expect that the call for upgrade should just continue the flow of data. However, a simple test tells me that there is something very wrong:
Length = 256 newaes = OpenSSL::Cipher::Cipher.new('aes-256-cbc') newaes.encrypt newaes.key= Digest::SHA256.digest("foo") puts Base64.encode64(newaes.update("a"*Length)) puts Base64.encode64(newaes.update("a"*Length)) puts Base64.encode64(newaes.final)
Running this with different values for Length should not give me different threads. However, after completing the first update, there is always a problem. Streams diverge. I assumed that the problem is that for some inexplicable reason, the terminating null character ('\ 0') at the end of the line was encrypted. In the end, each call to update returns a string that is ((string.length / 16) + 1) * 16 bytes, implying that it encrypts an additional byte with each update.
How can I get OpenSSL encryption and decryption to work in a mode in which I can pass in data blocks and get the same result back, regardless of the size of the pieces that I break into data?
EDIT:
The problem does not depend on base64 encoding. Below are 3 different cyphertext results:
require 'digest/sha2' require 'base64' require 'openssl' def base64(data) Base64.encode64(data).chomp end def crypt_test(blocksize) newaes = OpenSSL::Cipher::Cipher.new('aes-256-cbc') newaes.encrypt newaes.key= Digest::SHA256.digest("foo") plaintext = "" cyphertext = "" File.open("black_bar.jpg") do |fd| while not fd.eof data = fd.read(blocksize) cyphertext += data cyphertext += newaes.update(data) end end cyphertext += newaes.final puts base64(Digest::SHA256.digest(plaintext)) puts base64(Digest::SHA256.digest(cyphertext)) puts end crypt_test(1024) crypt_test(512) crypt_test(2048)