twofish.rb
Martin Carpenter
mcarpenter@free.fr
Copyright © Martin Carpenter 2009
Implements a class for symmetric encryption using the Twofish encryption algorithm based on original work by Guido Flohr.
The block size in bytes (16).
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.
Return the cipher’s block size in bytes.
# File lib/twofish.rb, line 465 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) 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
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 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 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 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
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
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 or Padding::ZERO_BYTE == :zero_byte).
# File lib/twofish.rb, line 460 def padding=(scheme) @padding = Padding.validate(scheme) end
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
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
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