class Twofish

twofish.rb

Author

Martin Carpenter

Email

mcarpenter@free.fr

Copyright

Copyright © Martin Carpenter 2009

Implements a class for symmetric encryption using the Twofish encryption algorithm based on original work by Guido Flohr.

Constants

BLOCK_SIZE

The block size in bytes (16).

Attributes

iv[R]

Initialization vector for CBC mode.

key_size[R]

The size of the key in bytes (16, 24, 32 bytes).

mode[R]

Encryption mode eg Mode::ECB (default) or Mode::CBC.

padding[R]

Padding algorithm eg Padding::NONE (default) or Padding::ZERO_BYTE.

Public Class Methods

block_size() click to toggle source

Return the cipher’s block size in bytes.

# File lib/twofish.rb, line 465
def self.block_size
  BLOCK_SIZE
end
new(key_string, opts={}) click to toggle source

Takes a mandatory key (16, 24 or 32 bytes), and an options hash as follows:

:mode => :ecb (default) or :cbc
:iv => optional 16 byte initialization vector (randomly generated if not supplied)
:padding => :none (default) or :zero_byte
# File lib/twofish.rb, line 305
def initialize(key_string, opts={})

  self.mode = opts[:mode] # use setter for validation
  self.padding = opts[:padding] # use setter for validation
  @iv = opts[:iv] || generate_iv(BLOCK_SIZE) unless @mode == Mode::ECB

  # The key consists of k=len/8 (2, 3 or 4) 64-bit units.
  key = key_string.unpack("C*")
  @key_size = key.length

  # We must derive three vectors Me, Mo, and S, each with k 32-bit
  # words, from the 2k words in the key.
  #
  #       Me = (key[0], key[2], ..., key[2k-2]) (even words)
  #       Mo = (key[1], key[3], ..., key[2k-1]) (odd  words)
  #
  # The third vector is derived by multiplying each of the k groups
  # of 8 bytes from the key by a 4x8 matrix, to get k 32-bit words.
  #
  #       S = (S[k-1], S[k-2], ..., S[0])
  #
  # where S[i] are the 4 bytes from the multiplication, interpreted
  # as a 32-bit word. As described later, mds_rem is equivalent to
  # the matrix multiplication, but faster.
  le_longs = key_string.unpack("V*")

  # The words of the expanded key K are defined using the h function:
  #
  #       rho     = 2^24 + 2^16 + 2^8 + 2^0 (0x01010101)
  #       A[i]    = h(2i*rho, Me)
  #       B[i]    = ROL(h(2(i+1)*rho, Mo), 8)
  #       K[2i]   = (A[i] + B[i]) mod 2^32
  #       K[2i+1] = ROL((A[i] + 2B[i]) mod 2^32, 9)
  #
  # rho has the property that, for i = 0..255, the word i*rho
  # consists of four equal bytes, each with the value i. The function
  # h is only applied to words of this type, so we only pass it the
  # value of i.
  @k = []

  # The key-dependent S-boxes used in the g() function are created
  # below. They are defined by g(X) = h(X, S), where S is the vector
  # derived from the key. That is, for i=0..3, the S-box S[i] is
  # formed by mapping from x[i] to y[i] in the h function.
  #
  # The relevant lookup tables qN have been precomputed and stored in
  # tables.h; we also perform full key precomputations incorporating
  # the MDS matrix multiplications.
  @xS0, @xS1, @xS2, @xS3 = [], [], [], []
  case @key_size
  when 16
    s7, s6, s5, s4 = *mds_rem(le_longs[0], le_longs[1])
    s3, s2, s1, s0 = *mds_rem(le_longs[2], le_longs[3])
    (0..38).step(2) do |i|
      j = i + 1
      a = M0[Q0[Q0[i] ^ key[8]]  ^ key[0]] ^
          M1[Q0[Q1[i] ^ key[9]]  ^ key[1]] ^
          M2[Q1[Q0[i] ^ key[10]] ^ key[2]] ^
          M3[Q1[Q1[i] ^ key[11]] ^ key[3]]
      b = M0[Q0[Q0[j] ^ key[12]] ^ key[4]] ^
          M1[Q0[Q1[j] ^ key[13]] ^ key[5]] ^
          M2[Q1[Q0[j] ^ key[14]] ^ key[6]] ^
          M3[Q1[Q1[j] ^ key[15]] ^ key[7]]
      b = ((b & 0xffffff) << 8) | (b >> 24)
      a = 0xffffffff & (a+b)
      @k.push(a)
      a = 0xffffffff & (a+b)
      @k.push((a & 0x7fffff) << 9 | a >> 23)
    end
    (0..255).each do |i|
      @xS0[i] = M0[Q0[Q0[i] ^ s4] ^ s0]
      @xS1[i] = M1[Q0[Q1[i] ^ s5] ^ s1]
      @xS2[i] = M2[Q1[Q0[i] ^ s6] ^ s2]
      @xS3[i] = M3[Q1[Q1[i] ^ s7] ^ s3]
    end
  when 24
    sb, sa, s9, s8 = *mds_rem(le_longs[0], le_longs[1])
    s7, s6, s5, s4 = *mds_rem(le_longs[2], le_longs[3])
    s3, s2, s1, s0 = *mds_rem(le_longs[4], le_longs[5])
    (0..38).step(2) do |i|
      j = i + 1
      a = M0[Q0[Q0[Q1[i] ^ key[16]] ^ key[8]]  ^ key[0]] ^
          M1[Q0[Q1[Q1[i] ^ key[17]] ^ key[9]]  ^ key[1]] ^
          M2[Q1[Q0[Q0[i] ^ key[18]] ^ key[10]] ^ key[2]] ^
          M3[Q1[Q1[Q0[i] ^ key[19]] ^ key[11]] ^ key[3]]
      b = M0[Q0[Q0[Q1[j] ^ key[20]] ^ key[12]] ^ key[4]] ^
          M1[Q0[Q1[Q1[j] ^ key[21]] ^ key[13]] ^ key[5]] ^
          M2[Q1[Q0[Q0[j] ^ key[22]] ^ key[14]] ^ key[6]] ^
          M3[Q1[Q1[Q0[j] ^ key[23]] ^ key[15]] ^ key[7]]
      b = ((b & 0xffffff) << 8) | (b >> 24)
      a = 0xffffffff & (a+b)
      @k.push(a)
      a = 0xffffffff & (a+b)
      @k.push((a & 0x7fffff) << 9 | a >> 23)
    end
    (0..255).each do |i|
      @xS0[i] = M0[Q0[Q0[Q1[i] ^ s8] ^ s4] ^ s0]
      @xS1[i] = M1[Q0[Q1[Q1[i] ^ s9] ^ s5] ^ s1]
      @xS2[i] = M2[Q1[Q0[Q0[i] ^ sa] ^ s6] ^ s2]
      @xS3[i] = M3[Q1[Q1[Q0[i] ^ sb] ^ s7] ^ s3]
    end
  when 32
    sf, se, sd, sc = *mds_rem(le_longs[0], le_longs[1])
    sb, sa, s9, s8 = *mds_rem(le_longs[2], le_longs[3])
    s7, s6, s5, s4 = *mds_rem(le_longs[4], le_longs[5])
    s3, s2, s1, s0 = *mds_rem(le_longs[6], le_longs[7])
    (0..38).step(2) do |i|
      j = i + 1
      a = M0[Q0[Q0[Q1[Q1[i] ^ key[24]] ^ key[16]] ^ key[8]]  ^ key[0]] ^
          M1[Q0[Q1[Q1[Q0[i] ^ key[25]] ^ key[17]] ^ key[9]]  ^ key[1]] ^
          M2[Q1[Q0[Q0[Q0[i] ^ key[26]] ^ key[18]] ^ key[10]] ^ key[2]] ^
          M3[Q1[Q1[Q0[Q1[i] ^ key[27]] ^ key[19]] ^ key[11]] ^ key[3]]
      b = M0[Q0[Q0[Q1[Q1[j] ^ key[28]] ^ key[20]] ^ key[12]] ^ key[4]] ^
          M1[Q0[Q1[Q1[Q0[j] ^ key[29]] ^ key[21]] ^ key[13]] ^ key[5]] ^
          M2[Q1[Q0[Q0[Q0[j] ^ key[30]] ^ key[22]] ^ key[14]] ^ key[6]] ^
          M3[Q1[Q1[Q0[Q1[j] ^ key[31]] ^ key[23]] ^ key[15]] ^ key[7]]
      b = ((b & 0xffffff) << 8) | (b >> 24)
      a = 0xffffffff & (a+b)
      @k.push(a)
      a = 0xffffffff & (a+b)
      @k.push((a & 0x7fffff) << 9 | a >> 23)
    end
    (0..255).each do |i|
      @xS0[i] = M0[Q0[Q0[Q1[Q1[i]^sc]^s8]^s4]^s0]
      @xS1[i] = M1[Q0[Q1[Q1[Q0[i]^sd]^s9]^s5]^s1]
      @xS2[i] = M2[Q1[Q0[Q0[Q0[i]^se]^sa]^s6]^s2]
      @xS3[i] = M3[Q1[Q1[Q0[Q1[i]^sf]^sb]^s7]^s3]
    end
  else
    raise ArgumentError, "invalid key length #{@key_size} (expecting 16, 24 or 32 bytes)"
  end

end

Public Instance Methods

decrypt(ciphertext) click to toggle source

Decrypt a ciphertext string, unchunking as required for chaining modes. If @iv is not set then we use the first block as the initialization vector when chaining.

# File lib/twofish.rb, line 490
def decrypt(ciphertext)
  ciphertext = ciphertext.dup.force_encoding('ASCII-8BIT')
  raise ArgumentError, "ciphertext is not a multiple of #{BLOCK_SIZE} bytes" unless (ciphertext.length % BLOCK_SIZE).zero?
  result = ''.force_encoding('ASCII-8BIT')
  if Mode::CBC == @mode
    if @iv
      feedback = @iv
    else
      feedback = ciphertext[0, BLOCK_SIZE]
      ciphertext = ciphertext[BLOCK_SIZE..-1]
    end
  end
  (0...ciphertext.length).step(BLOCK_SIZE) do |block_ptr|
    ciphertext_block = ciphertext[block_ptr, BLOCK_SIZE]
    plaintext_block = decrypt_block(ciphertext_block)
    xor_block!(plaintext_block, feedback) if Mode::CBC == @mode
    result << plaintext_block
    feedback = ciphertext_block
  end
  Padding.unpad!(result, BLOCK_SIZE, @padding)
end
decrypt_block(plain) click to toggle source

Decrypt a single block (16 bytes).

# File lib/twofish.rb, line 780
def decrypt_block(plain)

  words = plain.unpack("V4")

  r0 = @k[4] ^ words[0]
  r1 = @k[5] ^ words[1]
  r2 = @k[6] ^ words[2]
  r3 = @k[7] ^ words[3]

  # i = 7
  t0 = @xS0[r0 & 0xff] ^
       @xS1[r0 >> 8 & 0xff] ^
       @xS2[r0 >> 16 & 0xff] ^
       @xS3[r0 >> 24 & 0xff]
  t1 = @xS0[r1 >> 24 & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[r1 >> 8 & 0xff] ^
       @xS3[r1 >> 16 & 0xff]

  r2 = r2 >> 31 & 0x1 | (r2 & 0x7fffffff) << 1
  r2 ^= 0xffffffff & (t0 + t1 + @k[38])

  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[39])
  r3 = r3 >> 1 & 0x7fffffff | (r3 & 0x1) << 31

  t0 = @xS0[r2 & 0xff] ^
       @xS1[r2 >> 8 & 0xff] ^
       @xS2[r2 >> 16 & 0xff] ^
       @xS3[r2 >> 24 & 0xff]
  t1 = @xS0[r3 >> 24 & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[r3 >> 8 & 0xff] ^
       @xS3[r3 >> 16 & 0xff]

  r0 = r0 >> 31 & 0x1 | (r0 & 0x7fffffff) << 1
  r0 ^= 0xffffffff & (t0 + t1 + @k[36])

  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[37])
  r1 = r1 >> 1 & 0x7fffffff | (r1 & 0x1) << 31

  # i = 6
  t0 = @xS0[r0 & 0xff] ^
       @xS1[r0 >> 8 & 0xff] ^
       @xS2[r0 >> 16 & 0xff] ^
       @xS3[r0 >> 24 & 0xff]
  t1 = @xS0[r1 >> 24 & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[r1 >> 8 & 0xff] ^
       @xS3[r1 >> 16 & 0xff]

  r2 = r2 >> 31 & 0x1 | (r2 & 0x7fffffff) << 1
  r2 ^= 0xffffffff & (t0 + t1 + @k[34])

  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[35])
  r3 = r3 >> 1 & 0x7fffffff | (r3 & 0x1) << 31

  t0 = @xS0[r2 & 0xff] ^
       @xS1[r2 >> 8 & 0xff] ^
       @xS2[r2 >> 16 & 0xff] ^
       @xS3[r2 >> 24 & 0xff]
  t1 = @xS0[r3 >> 24 & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[r3 >> 8 & 0xff] ^
       @xS3[r3 >> 16 & 0xff]

  r0 = r0 >> 31 & 0x1 | (r0 & 0x7fffffff) << 1
  r0 ^= 0xffffffff & (t0 + t1 + @k[32])

  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[33])
  r1 = r1 >> 1 & 0x7fffffff | (r1 & 0x1) << 31

  # i = 5
  t0 = @xS0[r0 & 0xff] ^
       @xS1[r0 >> 8 & 0xff] ^
       @xS2[r0 >> 16 & 0xff] ^
       @xS3[r0 >> 24 & 0xff]
  t1 = @xS0[r1 >> 24 & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[r1 >> 8 & 0xff] ^
       @xS3[r1 >> 16 & 0xff]

  r2 = r2 >> 31 & 0x1 | (r2 & 0x7fffffff) << 1
  r2 ^= 0xffffffff & (t0 + t1 + @k[30])

  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[31])
  r3 = r3 >> 1 & 0x7fffffff | (r3 & 0x1) << 31

  t0 = @xS0[r2 & 0xff] ^
       @xS1[r2 >> 8 & 0xff] ^
       @xS2[r2 >> 16 & 0xff] ^
       @xS3[r2 >> 24 & 0xff]
  t1 = @xS0[r3 >> 24 & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[r3 >> 8 & 0xff] ^
       @xS3[r3 >> 16 & 0xff]

  r0 = r0 >> 31 & 0x1 | (r0 & 0x7fffffff) << 1
  r0 ^= 0xffffffff & (t0 + t1 + @k[28])

  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[29])
  r1 = r1 >> 1 & 0x7fffffff | (r1 & 0x1) << 31

  # i = 4
  t0 = @xS0[r0 & 0xff] ^
       @xS1[r0 >> 8 & 0xff] ^
       @xS2[r0 >> 16 & 0xff] ^
       @xS3[r0 >> 24 & 0xff]
  t1 = @xS0[r1 >> 24 & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[r1 >> 8 & 0xff] ^
       @xS3[r1 >> 16 & 0xff]

  r2 = r2 >> 31 & 0x1 | (r2 & 0x7fffffff) << 1
  r2 ^= 0xffffffff & (t0 + t1 + @k[26])

  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[27])
  r3 = r3 >> 1 & 0x7fffffff | (r3 & 0x1) << 31

  t0 = @xS0[r2 & 0xff] ^
       @xS1[r2 >> 8 & 0xff] ^
       @xS2[r2 >> 16 & 0xff] ^
       @xS3[r2 >> 24 & 0xff]
  t1 = @xS0[r3 >> 24 & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[r3 >> 8 & 0xff] ^
       @xS3[r3 >> 16 & 0xff]

  r0 = r0 >> 31 & 0x1 | (r0 & 0x7fffffff) << 1
  r0 ^= 0xffffffff & (t0 + t1 + @k[24])

  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[25])
  r1 = r1 >> 1 & 0x7fffffff | (r1 & 0x1) << 31

  # i = 3
  t0 = @xS0[r0 & 0xff] ^
       @xS1[r0 >> 8 & 0xff] ^
       @xS2[r0 >> 16 & 0xff] ^
       @xS3[r0 >> 24 & 0xff]
  t1 = @xS0[r1 >> 24 & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[r1 >> 8 & 0xff] ^
       @xS3[r1 >> 16 & 0xff]

  r2 = r2 >> 31 & 0x1 | (r2 & 0x7fffffff) << 1
  r2 ^= 0xffffffff & (t0 + t1 + @k[22])

  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[23])
  r3 = r3 >> 1 & 0x7fffffff | (r3 & 0x1) << 31

  t0 = @xS0[r2 & 0xff] ^
       @xS1[r2 >> 8 & 0xff] ^
       @xS2[r2 >> 16 & 0xff] ^
       @xS3[r2 >> 24 & 0xff]
  t1 = @xS0[r3 >> 24 & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[r3 >> 8 & 0xff] ^
       @xS3[r3 >> 16 & 0xff]

  r0 = r0 >> 31 & 0x1 | (r0 & 0x7fffffff) << 1
  r0 ^= 0xffffffff & (t0 + t1 + @k[20])

  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[21])
  r1 = r1 >> 1 & 0x7fffffff | (r1 & 0x1) << 31

  # i = 2
  t0 = @xS0[r0 & 0xff] ^
       @xS1[r0 >> 8 & 0xff] ^
       @xS2[r0 >> 16 & 0xff] ^
       @xS3[r0 >> 24 & 0xff]
  t1 = @xS0[r1 >> 24 & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[r1 >> 8 & 0xff] ^
       @xS3[r1 >> 16 & 0xff]

  r2 = r2 >> 31 & 0x1 | (r2 & 0x7fffffff) << 1
  r2 ^= 0xffffffff & (t0 + t1 + @k[18])

  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[19])
  r3 = r3 >> 1 & 0x7fffffff | (r3 & 0x1) << 31

  t0 = @xS0[r2 & 0xff] ^
       @xS1[r2 >> 8 & 0xff] ^
       @xS2[r2 >> 16 & 0xff] ^
       @xS3[r2 >> 24 & 0xff]
  t1 = @xS0[r3 >> 24 & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[r3 >> 8 & 0xff] ^
       @xS3[r3 >> 16 & 0xff]

  r0 = r0 >> 31 & 0x1 | (r0 & 0x7fffffff) << 1
  r0 ^= 0xffffffff & (t0 + t1 + @k[16])

  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[17])
  r1 = r1 >> 1 & 0x7fffffff | (r1 & 0x1) << 31

  # i = 1
  t0 = @xS0[r0 & 0xff] ^
       @xS1[r0 >> 8 & 0xff] ^
       @xS2[r0 >> 16 & 0xff] ^
       @xS3[r0 >> 24 & 0xff]
  t1 = @xS0[r1 >> 24 & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[r1 >> 8 & 0xff] ^
       @xS3[r1 >> 16 & 0xff]

  r2 = r2 >> 31 & 0x1 | (r2 & 0x7fffffff) << 1
  r2 ^= 0xffffffff & (t0 + t1 + @k[14])

  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[15])
  r3 = r3 >> 1 & 0x7fffffff | (r3 & 0x1) << 31

  t0 = @xS0[r2 & 0xff] ^
       @xS1[r2 >> 8 & 0xff] ^
       @xS2[r2 >> 16 & 0xff] ^
       @xS3[r2 >> 24 & 0xff]
  t1 = @xS0[r3 >> 24 & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[r3 >> 8 & 0xff] ^
       @xS3[r3 >> 16 & 0xff]

  r0 = r0 >> 31 & 0x1 | (r0 & 0x7fffffff) << 1
  r0 ^= 0xffffffff & (t0 + t1 + @k[12])

  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[13])
  r1 = r1 >> 1 & 0x7fffffff | (r1 & 0x1) << 31

  # i = 0
  t0 = @xS0[r0 & 0xff] ^
       @xS1[r0 >> 8 & 0xff] ^
       @xS2[r0 >> 16 & 0xff] ^
       @xS3[r0 >> 24 & 0xff]
  t1 = @xS0[r1 >> 24 & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[r1 >> 8 & 0xff] ^
       @xS3[r1 >> 16 & 0xff]

  r2 = r2 >> 31 & 0x1 | (r2 & 0x7fffffff) << 1
  r2 ^= 0xffffffff & (t0 + t1 + @k[10])

  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[11])
  r3 = r3 >> 1 & 0x7fffffff | (r3 & 0x1) << 31

  t0 = @xS0[r2 & 0xff] ^
       @xS1[r2 >> 8 & 0xff] ^
       @xS2[r2 >> 16 & 0xff] ^
       @xS3[r2 >> 24 & 0xff]
  t1 = @xS0[r3 >> 24 & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[r3 >> 8 & 0xff] ^
       @xS3[r3 >> 16 & 0xff]

  r0 = r0 >> 31 & 0x1 | (r0 & 0x7fffffff) << 1
  r0 ^= 0xffffffff & (t0 + t1 + @k[8])

  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[9])
  r1 = r1 >> 1 & 0x7fffffff | (r1 & 0x1) << 31

  [@k[0] ^ r2, @k[1] ^ r3, @k[2] ^ r0, @k[3] ^ r1].pack("V4")
end
encrypt(plaintext) click to toggle source

Encrypt a plaintext string, chunking as required for CBC mode.

# File lib/twofish.rb, line 471
def encrypt(plaintext)
  plaintext = plaintext.dup.force_encoding('ASCII-8BIT')
  padded_plaintext = Padding.pad!(plaintext, BLOCK_SIZE, @padding)
  result = ''.force_encoding('ASCII-8BIT')
  if @mode == Mode::CBC
    @iv = generate_iv(BLOCK_SIZE) unless @iv
    ciphertext_block = @iv
  end
  (0...padded_plaintext.length).step(BLOCK_SIZE) do |block_ptr|
    plaintext_block = padded_plaintext[block_ptr, BLOCK_SIZE]
    xor_block!(plaintext_block, ciphertext_block) if Mode::CBC == @mode
    result << ciphertext_block = encrypt_block(plaintext_block)
  end
  result
end
encrypt_block(plain_text) click to toggle source

Encrypt a single block (16 bytes).

# File lib/twofish.rb, line 519
def encrypt_block(plain_text)

  words = plain_text.unpack('V4')

  r0 = @k[0] ^ words[0]
  r1 = @k[1] ^ words[1]
  r2 = @k[2] ^ words[2]
  r3 = @k[3] ^ words[3]

  # i = 0
  t0 = @xS0[r0 & 0xff] ^
       @xS1[(r0 >> 8) & 0xff] ^
       @xS2[(r0 >> 16) & 0xff] ^
       @xS3[(r0 >> 24) & 0xff]
  t1 = @xS0[(r1 >> 24) & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[(r1 >> 8) & 0xff] ^
       @xS3[(r1 >> 16) & 0xff]

  r2 ^= 0xffffffff & (t0 + t1 + @k[8])
  r2 = (r2 >> 1 & 0x7fffffff) | (r2 & 0x1) << 31

  r3 = ((r3 >> 31) & 1) | (r3 & 0x7fffffff) << 1
  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[9])

  t0 = @xS0[r2 & 0xff] ^
       @xS1[(r2 >> 8) & 0xff] ^
       @xS2[(r2 >> 16) & 0xff] ^
       @xS3[(r2 >> 24) & 0xff]
  t1 = @xS0[(r3 >> 24) & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[(r3 >> 8) & 0xff] ^
       @xS3[(r3 >> 16) & 0xff]

  r0 ^= 0xffffffff & (t0 + t1 + @k[10])
  r0 = (r0 >> 1 & 0x7fffffff) | (r0 & 0x1) << 31

  r1 = ((r1 >> 31) & 1) | (r1 & 0x7fffffff) << 1
  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[11])

  # i = 1
  t0 = @xS0[r0 & 0xff] ^
       @xS1[(r0 >> 8) & 0xff] ^
       @xS2[(r0 >> 16) & 0xff] ^
       @xS3[(r0 >> 24) & 0xff]
  t1 = @xS0[(r1 >> 24) & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[(r1 >> 8) & 0xff] ^
       @xS3[(r1 >> 16) & 0xff]

  r2 ^= 0xffffffff & (t0 + t1 + @k[12])
  r2 = (r2 >> 1 & 0x7fffffff) | (r2 & 0x1) << 31

  r3 = ((r3 >> 31) & 1) | (r3 & 0x7fffffff) << 1
  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[13])

  t0 = @xS0[r2 & 0xff] ^
       @xS1[(r2 >> 8) & 0xff] ^
       @xS2[(r2 >> 16) & 0xff] ^
       @xS3[(r2 >> 24) & 0xff]
  t1 = @xS0[(r3 >> 24) & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[(r3 >> 8) & 0xff] ^
       @xS3[(r3 >> 16) & 0xff]

  r0 ^= 0xffffffff & (t0 + t1 + @k[14])
  r0 = (r0 >> 1 & 0x7fffffff) | (r0 & 0x1) << 31

  r1 = ((r1 >> 31) & 1) | (r1 & 0x7fffffff) << 1
  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[15])

  # i = 2
  t0 = @xS0[r0 & 0xff] ^
       @xS1[(r0 >> 8) & 0xff] ^
       @xS2[(r0 >> 16) & 0xff] ^
       @xS3[(r0 >> 24) & 0xff]
  t1 = @xS0[(r1 >> 24) & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[(r1 >> 8) & 0xff] ^
       @xS3[(r1 >> 16) & 0xff]

  r2 ^= 0xffffffff & (t0 + t1 + @k[16])
  r2 = (r2 >> 1 & 0x7fffffff) | (r2 & 0x1) << 31

  r3 = ((r3 >> 31) & 1) | (r3 & 0x7fffffff) << 1
  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[17])

  t0 = @xS0[r2 & 0xff] ^
       @xS1[(r2 >> 8) & 0xff] ^
       @xS2[(r2 >> 16) & 0xff] ^
       @xS3[(r2 >> 24) & 0xff]
  t1 = @xS0[(r3 >> 24) & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[(r3 >> 8) & 0xff] ^
       @xS3[(r3 >> 16) & 0xff]

  r0 ^= 0xffffffff & (t0 + t1 + @k[18])
  r0 = (r0 >> 1 & 0x7fffffff) | (r0 & 0x1) << 31

  r1 = ((r1 >> 31) & 1) | (r1 & 0x7fffffff) << 1
  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[19])

  # i = 3
  t0 = @xS0[r0 & 0xff] ^
       @xS1[(r0 >> 8) & 0xff] ^
       @xS2[(r0 >> 16) & 0xff] ^
       @xS3[(r0 >> 24) & 0xff]
  t1 = @xS0[(r1 >> 24) & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[(r1 >> 8) & 0xff] ^
       @xS3[(r1 >> 16) & 0xff]

  r2 ^= 0xffffffff & (t0 + t1 + @k[20])
  r2 = (r2 >> 1 & 0x7fffffff) | (r2 & 0x1) << 31

  r3 = ((r3 >> 31) & 1) | ((r3 & 0x7fffffff) << 1)
  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[21])

  t0 = @xS0[r2 & 0xff] ^
       @xS1[(r2 >> 8) & 0xff] ^
       @xS2[(r2 >> 16) & 0xff] ^
       @xS3[(r2 >> 24) & 0xff]
  t1 = @xS0[(r3 >> 24) & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[(r3 >> 8) & 0xff] ^
       @xS3[(r3 >> 16) & 0xff]

  r0 ^= 0xffffffff & (t0 + t1 + @k[22])
  r0 = (r0 >> 1 & 0x7fffffff) | (r0 & 0x1) << 31

  r1 = ((r1 >> 31) & 1) | (r1 & 0x7fffffff) << 1
  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[23])

  # i = 4
  t0 = @xS0[r0 & 0xff] ^
       @xS1[(r0 >> 8) & 0xff] ^
       @xS2[(r0 >> 16) & 0xff] ^
       @xS3[(r0 >> 24) & 0xff]
  t1 = @xS0[(r1 >> 24) & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[(r1 >> 8) & 0xff] ^
       @xS3[(r1 >> 16) & 0xff]

  r2 ^= 0xffffffff & (t0 + t1 + @k[24])
  r2 = (r2 >> 1 & 0x7fffffff) | (r2 & 0x1) << 31

  r3 = ((r3 >> 31) & 1) | (r3 & 0x7fffffff) << 1
  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[25])

  t0 = @xS0[r2 & 0xff] ^
       @xS1[(r2 >> 8) & 0xff] ^
       @xS2[(r2 >> 16) & 0xff] ^
       @xS3[(r2 >> 24) & 0xff]
  t1 = @xS0[(r3 >> 24) & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[(r3 >> 8) & 0xff] ^
       @xS3[(r3 >> 16) & 0xff]

  r0 ^= 0xffffffff & (t0 + t1 + @k[26])
  r0 = (r0 >> 1 & 0x7fffffff) | (r0 & 0x1) << 31

  r1 = ((r1 >> 31) & 1) | ((r1 & 0x7fffffff) << 1)
  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[27])

  # i = 5
  t0 = @xS0[r0 & 0xff] ^
       @xS1[(r0 >> 8) & 0xff] ^
       @xS2[(r0 >> 16) & 0xff] ^
       @xS3[(r0 >> 24) & 0xff]
  t1 = @xS0[(r1 >> 24) & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[(r1 >> 8) & 0xff] ^
       @xS3[(r1 >> 16) & 0xff]

  r2 ^= 0xffffffff & (t0 + t1 + @k[28])
  r2 = (r2 >> 1 & 0x7fffffff) | (r2 & 0x1) << 31

  r3 = ((r3 >> 31) & 1) | (r3 & 0x7fffffff) << 1
  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[29])

  t0 = @xS0[r2 & 0xff] ^
       @xS1[(r2 >> 8) & 0xff] ^
       @xS2[(r2 >> 16) & 0xff] ^
       @xS3[(r2 >> 24) & 0xff]
  t1 = @xS0[(r3 >> 24) & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[(r3 >> 8) & 0xff] ^
       @xS3[(r3 >> 16) & 0xff]

  r0 ^= 0xffffffff & (t0 + t1 + @k[30])
  r0 = (r0 >> 1 & 0x7fffffff) | (r0 & 0x1) << 31

  r1 = ((r1 >> 31) & 1) | (r1 & 0x7fffffff) << 1
  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[31])

  # i = 6
  t0 = @xS0[r0 & 0xff] ^
       @xS1[(r0 >> 8) & 0xff] ^
       @xS2[(r0 >> 16) & 0xff] ^
       @xS3[(r0 >> 24) & 0xff]
  t1 = @xS0[(r1 >> 24) & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[(r1 >> 8) & 0xff] ^
       @xS3[(r1 >> 16) & 0xff]

  r2 ^= 0xffffffff & (t0 + t1 + @k[32])
  r2 = (r2 >> 1 & 0x7fffffff) | (r2 & 0x1) << 31

  r3 = ((r3 >> 31) & 1) | (r3 & 0x7fffffff) << 1
  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[33])

  t0 = @xS0[r2 & 0xff] ^
       @xS1[(r2 >> 8) & 0xff] ^
       @xS2[(r2 >> 16) & 0xff] ^
       @xS3[(r2 >> 24) & 0xff]
  t1 = @xS0[(r3 >> 24) & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[(r3 >> 8) & 0xff] ^
       @xS3[(r3 >> 16) & 0xff]

  r0 ^= 0xffffffff & (t0 + t1 + @k[34])
  r0 = (r0 >> 1 & 0x7fffffff) | (r0 & 0x1) << 31

  r1 = ((r1 >> 31) & 1) | (r1 & 0x7fffffff) << 1
  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[35])

  # i = 7
  t0 = @xS0[r0 & 0xff] ^
       @xS1[(r0 >> 8) & 0xff] ^
       @xS2[(r0 >> 16) & 0xff] ^
       @xS3[(r0 >> 24) & 0xff]
  t1 = @xS0[(r1 >> 24) & 0xff] ^
       @xS1[r1 & 0xff] ^
       @xS2[(r1 >> 8) & 0xff] ^
       @xS3[(r1 >> 16) & 0xff]

  r2 ^= 0xffffffff & (t0 + t1 + @k[36])
  r2 = (r2 >> 1 & 0x7fffffff) | (r2 & 0x1) << 31

  r3 = ((r3 >> 31) & 1) | (r3 & 0x7fffffff) << 1
  r3 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[37])

  t0 = @xS0[r2 & 0xff] ^
       @xS1[(r2 >> 8) & 0xff] ^
       @xS2[(r2 >> 16) & 0xff] ^
       @xS3[(r2 >> 24) & 0xff]
  t1 = @xS0[(r3 >> 24) & 0xff] ^
       @xS1[r3 & 0xff] ^
       @xS2[(r3 >> 8) & 0xff] ^
       @xS3[(r3 >> 16) & 0xff]

  r0 ^= 0xffffffff & (t0 + t1 + @k[38])
  r0 = (r0 >> 1 & 0x7fffffff) | (r0 & 0x1) << 31

  r1 = ((r1 >> 31) & 1) | (r1 & 0x7fffffff) << 1
  r1 ^= 0xffffffff & (t0 + ((t1 & 0x7fffffff) << 1) + @k[39])

  [@k[4] ^ r2, @k[5] ^ r3, @k[6] ^ r0, @k[7] ^ r1].pack("V4")
end
iv=(iv) click to toggle source

Assign the initialization vector. This does not make sense for ECB mode. Also the IV length must be a multiple of the block size.

# File lib/twofish.rb, line 442
def iv=(iv)
  raise ArgumentError, 'cannot specify initialization vector for ECB mode' if @mode == Mode::ECB
  raise ArgumentError, "initialization vectcor is not a multiple of #{BLOCK_SIZE} bytes" unless (iv.length % BLOCK_SIZE).zero?
  @iv = iv
end
mode=(mode) click to toggle source

Set the mode of the cipher (Mode::ECB == :ecb or Mode::CBC == :cbc). If the cipher has an IV already and mode is now set to Mode::ECB then the IV will be ignored for any encrypt/decrypt operations.

# File lib/twofish.rb, line 453
def mode=(mode)
  @mode = Mode.validate(mode)
end
padding=(scheme) click to toggle source

Set the padding scheme for the (CBC mode) cipher (Padding::NONE == :none or Padding::ZERO_BYTE == :zero_byte).

# File lib/twofish.rb, line 460
def padding=(scheme)
  @padding = Padding.validate(scheme)
end
xor_block!(target, source) click to toggle source

Exclusive-or two blocks together, byte-by-byte, storing the result in the first block.

# File lib/twofish.rb, line 514
def xor_block!(target, source)
  (0...BLOCK_SIZE).each { |i| target[i] = (target[i].ord ^ source[i].ord).chr }
end

Private Instance Methods

generate_iv(block_size) click to toggle source

Generates a random initialization vector of the given length. Warning: use Ruby standard library Kernel#rand.

# File lib/twofish.rb, line 1102
def generate_iv(block_size)
  Array.new(block_size).
    map{ |x| rand(256) }.
    pack("C#{block_size}") # defaults to ASCII-8BIT encoding
end
mds_rem(a, b) click to toggle source

The (12, 8) Reed Solomon code has the generator polynomial:

g(x) = x^4 + (a + 1/a) * x^3 + a * x^2 + (a + 1/a) * x + 1

where the coefficients are in the finite field GF(2^8) with a modular polynomial a^8+a^6+a^3+a^2+1. To generate the remainder, we have to start with a 12th order polynomial with our eight input bytes as the coefficients of the 4th to 11th terms:

m[7] * x^11 + m[6] * x^10 ... + m[0] * x^4 + 0 * x^3 +... + 0

We then multiply the generator polynomial by m*x^7 and subtract it (XOR in GF(2^8)) from the above to eliminate the x^7 term (the arithmetic on the coefficients is done in GF(2^8)). We then multiply the generator polynomial by m*x^6 and use this to remove the x^10 term, and so on until the x^4 term is removed, and we are left with:

r[3] * x^3 + r[2] * x^2 + r[1] 8 x^1 + r[0]

which give the resulting 4 bytes of the remainder. This is equivalent to the matrix multiplication described in the Twofish paper, but is much faster.

# File lib/twofish.rb, line 1064
def mds_rem(a, b)

  # use constant G_MOD => 0x14d

  t, u = 0, 0

  # No gain by unrolling this loop.
  8.times do
    t = b >> 24

    # Shift the others up.
    b = ((b & 0xffffff) << 8) | (a >> 24)
    a = ((a & 0xffffff) << 8)

    u = ((t & 0x7fffffff) << 1)

    # Subtract the modular polynomial on overflow.
    u ^= 0x14d unless (t & 0x80).zero?

    # Remove t * (a * x^2 + 1).
    b ^= t ^ ((u & 0xffff) << 16)

    # Form u = a*t + t/a = t*(a + 1/a).
    u ^= 0x7fffffff & (t >> 1)

    # Add the modular polynomial on underflow.
    u ^= 0xa6 unless (t & 0x01).zero?

    # Remove t * (a + 1/a) * (x^3 + x).
    b ^= ((u & 0xff) << 24) | ((u & 0xffffff) << 8)

  end

  [ b >> 24, b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff ]
end