Search code examples
rubyencryptionopenssl

Ruby openssl encryption with DES-CBC incorrect result


I am trying to replicate the encryption result from here in Ruby using OpenSSL: https://emvlab.org/descalc/?key=18074F7ADD44C903&iv=18074F7ADD44C903&input=4E5A56564F4C563230313641454E5300&mode=cbc&action=Encrypt&output=25C843BA5C043FFFB50F76E43A211F8D

  • Original string = "NZVVOLV2016AENS"
  • String converted to hexadecimal = "4e5a56564f4c563230313641454e53"
  • iv = "18074F7ADD44C903"
  • key = "18074F7ADD44C903"
  • Expected result = "9B699B4C59F1444E8D37806FA9D15F81"

Here is my ruby code:

require 'openssl'
require "base64"
include Base64

iv = "08074F7ADD44C903"
cipher = "08074F7ADD44C903"

def encode(string)
  puts "Attempting encryption - Input: #{string}"
  encrypt = OpenSSL::Cipher.new('DES-CBC')
  encrypt.encrypt
  encrypt.key = ["18074F7ADD44C903"].pack('H*') #.scan(/../).map{|b|b.hex}.pack('c*')
  encrypt.iv = ["18074F7ADD44C903"].pack('H*')
  result = encrypt.update(string) + encrypt.final
  puts "Raw output: #{result.inspect}"
  unpacked = result.unpack('H*')[0]
  puts "Encrypted key is: #{unpacked}"
  puts "Encrypted Should be: 9B699B4C59F1444E8D37806FA9D15F81"
  return unpacked
end

res = encode("NZVVOLV2016AENS")
Output:
Encrypted key is:    9b699b4c59f1444ea723ab91e89c023a
Encrypted Should be: 9B699B4C59F1444E8D37806FA9D15F81

Interestingly, the first half of the result is correct, and the last 16 digits are incorrect.


Solution

  • The web site uses Zero padding by default, while the Ruby code uses PKCS#7 padding by default.
    Ruby does not seem to support Zero padding, so disable the default padding and implement Zero padding yourself.
    Zero padding pads to the next full block size with 0x00 values. The block size for DES is 8 bytes. If the last block of the plaintext is already filled, no padding is done:

    def zeroPad(string, blocksize)
      len = string.bytes.length
      padLen = (blocksize - len % blocksize) % blocksize
      string += "\0" * padLen
      return string
    end
    

    In the encode() function (which should better be called encrypt() function) the following lines must be added before encryption:

    encrypt.padding = 0         # disable PKCS#7 padding
    string = zeroPad(string, 8) # enable Zero padding
    

    The modified Ruby code then gives the same ciphertext as the web site.


    Note that DES is insecure, also it' s insecure to use the key as IV (as well as a static IV). Furthermore, Zero padding is unreliable in contrast to PKCS#7 padding.