class Crypt::Rijndael

Public Class Methods

new(userKey, keyBits = 256, blockBits = 128) click to toggle source
# File lib/crypt/rijndael.rb, line 18
def initialize(userKey, keyBits = 256, blockBits = 128)
  case keyBits
    when 128
      @keyWords = 4
    when 192
      @keyWords = 6
    when 256
      @keyWords = 8
    else raise "The key must be 128, 192, or 256 bits long."
  end

  case (keyBits >= blockBits) ? keyBits : blockBits
    when 128
      @rounds = 10
    when 192
      @rounds = 12
    when 256
      @rounds = 14
    else raise "The key and block sizes must be 128, 192, or 256 bits long."
  end

  case blockBits
    when 128
      @blockSize = 16
      @blockWords = 4
      @shiftIndex = 0
    when 192
      @blockSize = 24
      @blockWords = 6
      @shiftIndex = 1
    when 256
      @blockSize = 32
      @blockWords = 8
      @shiftIndex = 2
    else raise "The block size must be 128, 192, or 256 bits long."
  end

  uk = userKey.unpack('C'*userKey.length)
  maxUsefulSizeOfUserKey = (keyBits/8)
  uk = uk[0..maxUsefulSizeOfUserKey-1]    # truncate
  padding = 0
  if (userKey.length < keyBits/8)
    shortfallInUserKey = (keyBits/8 - userKey.length)
    shortfallInUserKey.times { uk << padding }
  end
  @key = [[], [], [], []]
  0.upto(uk.length-1) { |pos|
    @key[pos % 4][pos / 4] = uk[pos]
  }
  @roundKeys = generate_key_schedule(@key, keyBits, blockBits)
end

Public Instance Methods

add_round_key(blockArray, roundKey) click to toggle source
# File lib/crypt/rijndael.rb, line 86
def add_round_key(blockArray, roundKey)
0.upto(3) { |i|
  0.upto(@blockWords) { |j|
    blockArray[i][j] ^= roundKey[i][j]
  }
}
return(blockArray)
end
block_size() click to toggle source
# File lib/crypt/rijndael.rb, line 71
def block_size
  return(@blockSize) # needed for CBC
end
decrypt_block(block) click to toggle source
# File lib/crypt/rijndael.rb, line 255
def decrypt_block(block)
  raise "block must be #{@blockSize} bytes long" if (block.length() != @blockSize)
  blockArray = [[], [], [], []]
  0.upto(@blockSize - 1) { |pos|
    b = block[pos]
    b = b.ord() unless b.is_a?(Fixnum)  # make sure we have a byte, not a single-char string
    blockArray[pos % 4][pos / 4] = b
  }
  decryptedBlock = decrypt_byte_array(blockArray)
  decrypted = "".force_encoding("ASCII-8BIT")  # stop ruby 2 using Unicode
  0.upto(@blockSize - 1) { |pos|
    decrypted << decryptedBlock[pos % 4][pos / 4]
  }
  return(decrypted)
end
decrypt_byte_array(blockArray) click to toggle source
# File lib/crypt/rijndael.rb, line 238
def decrypt_byte_array(blockArray)
  # first special round without inverse_mix_columns
  # add_round_key is an involution - applying it a second time returns the original result
  blockArray = add_round_key(blockArray, @roundKeys[@rounds])
  blockArray = substitution(blockArray,Si)   # using inverse S-box
  blockArray = shift_rows(blockArray,1)
  (@rounds-1).downto(1) { |round|
    blockArray = add_round_key(blockArray, @roundKeys[round])
    blockArray = inverse_mix_columns(blockArray)
    blockArray = substitution(blockArray, Si)
    blockArray = shift_rows(blockArray, 1)
  }
  blockArray = add_round_key(blockArray, @roundKeys[0])
  return(blockArray)
end
encrypt_block(block) click to toggle source
# File lib/crypt/rijndael.rb, line 220
def encrypt_block(block)
  block = block.force_encoding("ASCII-8BIT") if block.is_a?(String) # affordance
  raise "block must be #{@blockSize} bytes long" if (block.length() != @blockSize)
  blockArray = [[], [], [], []]
  0.upto(@blockSize - 1) { |pos|
    b = block[pos]
    b = b.ord unless b.is_a?(Fixnum)
    blockArray[pos % 4][pos / 4] = b
  }
  encryptedBlock = encrypt_byte_array(blockArray)
  encrypted = "".force_encoding("ASCII-8BIT")  # stop ruby 2 using Unicode
  0.upto(@blockSize - 1) { |pos|
    encrypted << encryptedBlock[pos % 4][pos / 4]
  }
  return(encrypted)
end
encrypt_byte_array(blockArray) click to toggle source
# File lib/crypt/rijndael.rb, line 204
def encrypt_byte_array(blockArray)
  blockArray = add_round_key(blockArray, @roundKeys[0])
  1.upto(@rounds - 1) { |round|
    blockArray = substitution(blockArray, S)
    blockArray = shift_rows(blockArray, 0)
    blockArray = mix_columns(blockArray)
    blockArray = add_round_key(blockArray, @roundKeys[round])
  }
  # special round without mix_columns
  blockArray = substitution(blockArray,S)
  blockArray = shift_rows(blockArray,0)
  blockArray = add_round_key(blockArray, @roundKeys[@rounds])
  return(blockArray)
end
generate_key_schedule(k, keyBits, blockBits) click to toggle source
# File lib/crypt/rijndael.rb, line 149
def generate_key_schedule(k, keyBits, blockBits)
  tk = k[0..3][0..@keyWords-1]  # using slice to get a copy instead of a reference
  keySched = []
  (@rounds + 1).times { keySched << [[], [], [], []] }
  t = 0
  j = 0
  while ((j < @keyWords) && (t < (@rounds+1)*@blockWords))
    0.upto(3) { |i|
      keySched[t / @blockWords][i][t % @blockWords] = tk[i][j]
    }
    j += 1
    t += 1
  end
  # while not enough round key material collected, calculate new values
  rconIndex = 0
  while (t < (@rounds+1)*@blockWords)
    0.upto(3) { |i|
      tk[i][0] ^= S[tk[(i + 1) % 4][@keyWords - 1]]
    }
    tk[0][0] ^= Rcon[rconIndex]
    rconIndex = rconIndex.next
    if (@keyWords != 8)
      1.upto(@keyWords - 1) { |j|
        0.upto(3) { |i|
          tk[i][j] ^= tk[i][j-1];
        }
      }
    else
      1.upto(@keyWords/2 - 1) { |j|
        0.upto(3) { |i|
          tk[i][j] ^= tk[i][j-1]
        }
      }
      0.upto(3) { |i|
        tk[i][@keyWords/2] ^= S[tk[i][@keyWords/2 - 1]]
      }
      (@keyWords/2 + 1).upto(@keyWords - 1) { |j|
        0.upto(3) { |i|
          tk[i][j] ^= tk[i][j-1]
        }
      }
    end
    j = 0
    while ((j < @keyWords) && (t < (@rounds+1) * @blockWords))
      0.upto(3) { |i|
        keySched[t / @blockWords][i][t % @blockWords] = tk[i][j]
      }
      j += 1
      t += 1
    end
  end
  return(keySched)
end
inverse_mix_columns(blockArray) click to toggle source
# File lib/crypt/rijndael.rb, line 135
def inverse_mix_columns(blockArray)
  unmixed = [[], [], [], []]
  0.upto(@blockWords-1) { |j|
    0.upto(3) { |i|
      unmixed[i][j] = mul(0xe, blockArray[i][j]) ^
        mul(0xb, blockArray[(i + 1) % 4][j]) ^
        mul(0xd, blockArray[(i + 2) % 4][j]) ^
        mul(0x9, blockArray[(i + 3) % 4][j])
    }
  }
   return(unmixed)
end
mix_columns(blockArray) click to toggle source
# File lib/crypt/rijndael.rb, line 121
def mix_columns(blockArray)
  mixed = [[], [], [], []]
  0.upto(@blockWords-1) { |j|
    0.upto(3) { |i|
      mixed[i][j] = mul(2,blockArray[i][j]) ^
        mul(3,blockArray[(i + 1) % 4][j]) ^
        blockArray[(i + 2) % 4][j] ^
        blockArray[(i + 3) % 4][j]
    }
  }
  return(mixed)
end
mul(a, b) click to toggle source
# File lib/crypt/rijndael.rb, line 76
def mul(a, b)
  if ((a ==0) | (b == 0))
    result = 0
  else
    result = AlogTable[(LogTable[a] + LogTable[b]) % 255]
  end
  return(result)
end
shift_rows(blockArray, direction) click to toggle source
# File lib/crypt/rijndael.rb, line 96
def shift_rows(blockArray, direction)
  tmp = []
  1.upto(3) { |i|  # row zero remains unchanged
    0.upto(@blockWords-1) { |j|
      tmp[j] = blockArray[i][(j + Shifts[@shiftIndex][i][direction]) % @blockWords]
    }
    0.upto(@blockWords-1) { |j|
      blockArray[i][j] = tmp[j]
    }
  }
  return(blockArray)
end
substitution(blockArray, sBox) click to toggle source
# File lib/crypt/rijndael.rb, line 110
def substitution(blockArray, sBox)
  # replace every byte of the input with the byte at that position in the S-box
  0.upto(3) { |i|
    0.upto(@blockWords-1) { |j|
      blockArray[i][j] = sBox[blockArray[i][j]]
    }
  }
  return(blockArray)
end