class Twofish
twofish.rb
- Author
-
Martin Carpenter
-
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
Initialization vector for CBC mode.
The size of the key in bytes (16, 24, 32 bytes).
Encryption mode eg Mode::ECB (default) or Mode::CBC.
Padding
algorithm eg Padding::NONE (default) or Padding::ZERO_BYTE.
Public Class Methods
Return the cipher's block size in bytes.
# File lib/twofish.rb, line 466 def self.block_size BLOCK_SIZE end
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), :zero_byte, :iso10126_2 or :pkcs7
# 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] || SecureRandom.random_bytes(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 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 491 def decrypt(ciphertext) ciphertext = to_binary(ciphertext.dup) raise ArgumentError, "ciphertext is not a multiple of #{BLOCK_SIZE} bytes" unless (ciphertext.length % BLOCK_SIZE).zero? result = to_binary('') 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 a single block (16 bytes).
# File lib/twofish.rb, line 786 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 a plaintext string, chunking as required for CBC mode.
# File lib/twofish.rb, line 472 def encrypt(plaintext) plaintext = to_binary(plaintext.dup) padded_plaintext = Padding.pad!(plaintext, BLOCK_SIZE, @padding) result = to_binary('') if @mode == Mode::CBC @iv ||= SecureRandom.random_bytes(BLOCK_SIZE) @_feedback ||= @iv end (0...padded_plaintext.length).step(BLOCK_SIZE) do |block_ptr| plaintext_block = padded_plaintext[block_ptr, BLOCK_SIZE] xor_block!(plaintext_block, @_feedback) if Mode::CBC == @mode result << @_feedback = encrypt_block(plaintext_block) end result end
Encrypt a single block (16 bytes).
# File lib/twofish.rb, line 525 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
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 vector is not a multiple of #{BLOCK_SIZE} bytes" unless (iv.length % BLOCK_SIZE).zero? @iv = iv end
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
Set the padding scheme for the (CBC mode) cipher (Padding::NONE == :none, Padding::ZERO_BYTE == :zero_byte, Padding::ISO10126_2 == :iso10126_2, Padding::PKCS7 == :pkcs7).
# File lib/twofish.rb, line 461 def padding=(scheme) @padding = Padding.validate(scheme) end
Reset the cipher state (for feedback modes).
# File lib/twofish.rb, line 514 def reset! @_feedback = nil end
Exclusive-or two blocks together, byte-by-byte, storing the result in the first block.
# File lib/twofish.rb, line 520 def xor_block!(target, source) (0...BLOCK_SIZE).each { |i| target[i] = (target[i].ord ^ source[i].ord).chr } end
Private Instance Methods
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 1075 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
Force String encoding to binary/8-bit ASCII. NB mutates argument.
# File lib/twofish.rb, line 1049 def to_binary(s) s.respond_to?(:force_encoding) ? s.force_encoding('BINARY') : s end