Hello I have next implementation of rijdael_128 encyption/decription in ruby
require 'mcrypt'
class Crypt
attr_accessor :key, :iv
def initialize(key, iv = nil)
self.key = key
self.iv = iv
@enc = Mcrypt.new(:rijndael_128, :cbc, normalize_key, self.iv, :pkcs)
end
def encrypt(data)
@enc.encrypt(data)
end
def decrypt(data)
@enc.decrypt(data).gsub(/[^0-9#,]/, '') # clean up last \a symbols
end
protected
def normalize_key
return self.key if [16, 24, 32].include?(self.key.length)
if self.key.length < 16
self.key.split.pack('a16')
elsif self.key.length < 24
self.key.split.pack('a24')
elsif self.key.length < 32
self.key.split.pack('a32')
elsif self.key.length > 32
self.key[0..31]
end
end
end
Is there a way to implement this without mcrypt ? want to use Cipher, however I have different results when key length > 16
class Crypt2
attr_accessor :key, :iv
def initialize(key, iv = nil)
self.key = key
self.iv = iv
end
def encrypt(data)
cipher = OpenSSL::Cipher.new('AES-128-CBC')
cipher.encrypt
cipher
cipher.key = normalize_key
cipher.iv = self.iv
enc = cipher.update(data)
enc << cipher.final
end
protected
def normalize_key
return self.key if [16, 24, 32].include?(self.key.length)
if self.key.length < 16
self.key.split.pack('a16')
elsif self.key.length < 24
self.key.split.pack('a24')
elsif self.key.length < 32
self.key.split.pack('a32')
elsif self.key.length > 32
self.key[0..31]
end
end
end
Same results with key = "1234567890"
1.9.3-p547 :498 > key = "1234567890"
=> "1234567890"
1.9.3-p547 :499 > iv
=> "0000001409228008"
1.9.3-p547 :500 > data
=> "1409227523#143620#16502300493"
1.9.3-p547 :501 > Crypt.new(key,iv).encrypt(data)
=> "\xFB\x16\a\xFF\x9ED\xA8\xD7\x1F=k\x8E\xFFH\xB0\x17\x84:\x1Fa\xB8s\x14\x97%S\xF3\x1E_\xDF\xBB\x19"
1.9.3-p547 :502 > Crypt2.new(key,iv).encrypt(data)
=> "\xFB\x16\a\xFF\x9ED\xA8\xD7\x1F=k\x8E\xFFH\xB0\x17\x84:\x1Fa\xB8s\x14\x97%S\xF3\x1E_\xDF\xBB\x19"
Different results with larger key
1.9.3-p547 :503 > key = key * 2
=> "12345678901234567890"
1.9.3-p547 :504 > Crypt.new(key,iv).encrypt(data)
=> "\x1A\xE61\xD7\xC8;\xE0M\xFA\xD4~[\xBA7N\xD9\xB9\xE2\x94\x8C\xA89\x99\xD9}\x82,9\xFE\xF5\xFA\x00"
1.9.3-p547 :505 > Crypt2.new(key,iv).encrypt(data)
=> "10X.\"\xF3\xC3RO`\t\x17\xB43\"r\x87s\xCF\xEA\x93Y4z\xCC\xC9\xAFA\xA1\x80\xC9\xF7"
As I commented before, you are explicitly initializing Cipher
/mcrypt
with AES-128
/rijndael-128
. That means the encryption function expects a key with length 128 bit
/16 bytes
.
It's behavior is undefined when you pass a larger key. (It could throw an error, could shrink the key, could do encryption with the larger key, or could do something else.)
It seems that Cipher
and mcrypt
handle this case differently and hence give different outputs.
Tests:
I could not find any statement on that case in either of the docs and so did some research on my own. For reference, this is the test-code I used:
require "rubygems"
require "openssl"
require "mcrypt"
def encryptOpenSSL(iv, key, data, key_length)
cipher = OpenSSL::Cipher.new("AES-" + key_length.to_s + "-CBC")
cipher.encrypt
cipher.key = key
cipher.iv = iv
return (cipher.update(data) + cipher.final).unpack("H*").join()
end
def encryptMcrypt(iv, key, data)
cipher = Mcrypt.new(:rijndael_128, :cbc, key, iv, :pkcs)
return cipher.encrypt(data).unpack("H*").join()
end
# test parameters
data = "This is my test-data!"
key128 = "1234567890123456"
key256 = "1234567890123456abcdefghijklmnop"
iv = "0987654321098765"
# tests
puts "OpenSSL AES(128) key=128bit: " + encryptOpenSSL(iv, key128, data, 128)
puts "OpenSSL AES(128) key=256bit: " + encryptOpenSSL(iv, key256, data, 128)
puts "Mcrypt AES(128) key=128bit: " + encryptMcrypt(iv, key128, data)
puts "Mcrypt AES(128) key=256bit: " + encryptMcrypt(iv, key256, data)
puts "OpenSSL AES(256) key=256bit: " + encryptOpenSSL(iv, key256, data, 256)
Which outputs for me (on ruby 1.9.1
):
"OpenSSL AES(128) key=128bit: adffaed8c94ede8aa61138b3fe500e30a0 ..."
"OpenSSL AES(128) key=256bit: adffaed8c94ede8aa61138b3fe500e30a0 ..."
"Mcrypt AES(128) key=128bit: adffaed8c94ede8aa61138b3fe500e30a0 ..."
"Mcrypt AES(128) key=256bit: b07776231d1bfbd2dfe3f8a62affdc4223 ..."
"OpenSSL AES(256) key=256bit: b07776231d1bfbd2dfe3f8a62affdc4223 ..."
Conclusion:
Looking at the test results, you can easily see that Cipher
still uses only the first 128 bits
when you pass a larger key than expected.
Whereas mcrypt()
performs rijndael-256
encryption when you pass a 256 bit
key (even though you have set rijndael-128
before).
Solution:
Assuming you want Cipher
to encrypt with AES-256
when you pass a 256 bit
key, you could dynamically set the key_size
depending on your input key.length
. Like this:
# `* 8` to convert from `bytes` to `bits`
cipher = OpenSSL::Cipher.new('AES-' + (normalize_key.length * 8).to_s + '-CBC')