class BiCrypt
BiCrypt
¶ ↑
A simple two-way encryption class.
This class is based on algorithms given in Applied Cryptography 2nd Ed.
If subclassed, the new encryption class must provide three methods:
-
encrypt_block
(block) -
decrypt_block
(block)
Constants
- SBOX
These are the S-boxes given in Applied Cryptography 2nd Ed., p. 333
- ULONG
- VERSION
Attributes
sBox[R]
S-boxes.
Public Class Methods
new(userKey)
click to toggle source
# File lib/bicrypt.rb, line 61 def initialize(userKey) @sBox = SBOX if userKey.size < 8 userKey += '01234567' end #@sTable = precalculate_s_table() # derive the 32-byte key from the user-supplied key userKeyLength = userKey.length @key = userKey[0..31].unpack('C'*32) if (userKeyLength < 32) userKeyLength.upto(31) { @key << 0 } end end
Public Instance Methods
decrypt_file(cryptFilename, plainFilename)
click to toggle source
Decrypt an encrypted file.
# File lib/bicrypt.rb, line 141 def decrypt_file(cryptFilename, plainFilename) cryptFile = carefully_open_file(cryptFilename, 'rb') plainFile = carefully_open_file(plainFilename, 'wb+') decrypt_stream(cryptFile, plainFile) cryptFile.close unless cryptFile.closed? plainFile.close unless plainFile.closed? end
decrypt_stream(cryptStream, plainStream)
click to toggle source
Decrypt an encrypted IO stream.
# File lib/bicrypt.rb, line 114 def decrypt_stream(cryptStream, plainStream) # Cypher-block-chain mode chain = cryptStream.read(block_size()) while (block = cryptStream.read(block_size())) decrypted = decrypt_block(block) plainText = xor(decrypted, chain) plainStream.write(plainText) unless cryptStream.eof? chain = block end # write the final block, omitting the padding buffer = plainText.split('') remainingMessageBytes = buffer.last.unpack('C').first remainingMessageBytes.times { plainStream.write(buffer.shift) } end
decrypt_string(cryptText)
click to toggle source
Decrypt an encrypted string.
# File lib/bicrypt.rb, line 159 def decrypt_string(cryptText) cryptStream = StringIO.new(cryptText) plainStream = StringIO.new('') decrypt_stream(cryptStream, plainStream) plainText = plainStream.string return(plainText) end
encrypt_file(plainFilename, cryptFilename)
click to toggle source
Encrypt a file.
# File lib/bicrypt.rb, line 132 def encrypt_file(plainFilename, cryptFilename) plainFile = carefully_open_file(plainFilename, 'rb') cryptFile = carefully_open_file(cryptFilename, 'wb+') encrypt_stream(plainFile, cryptFile) plainFile.close unless plainFile.closed? cryptFile.close unless cryptFile.closed? end
encrypt_stream(plainStream, cryptStream)
click to toggle source
Encrypt an IO stream.
# File lib/bicrypt.rb, line 83 def encrypt_stream(plainStream, cryptStream) # Cypher-block-chain mode initVector = generate_initialization_vector(block_size() / 4) chain = encrypt_block(initVector) cryptStream.write(chain) while ((block = plainStream.read(block_size())) && (block.length == block_size())) block = xor(block, chain) encrypted = encrypt_block(block) cryptStream.write(encrypted) chain = encrypted end # write the final block # At most block_size()-1 bytes can be part of the message. # That means the final byte can be used to store the number of meaningful # bytes in the final block block = '' if block.nil? buffer = block.split('') remainingMessageBytes = buffer.length # we use 7-bit characters to avoid possible strange behavior on the Mac remainingMessageBytes.upto(block_size()-2) { buffer << rand(128).chr } buffer << remainingMessageBytes.chr block = buffer.join('') block = xor(block, chain) encrypted = encrypt_block(block) cryptStream.write(encrypted) end
encrypt_string(plainText)
click to toggle source
Encrypt a string.
# File lib/bicrypt.rb, line 150 def encrypt_string(plainText) plainStream = StringIO.new(plainText) cryptStream = StringIO.new('') encrypt_stream(plainStream, cryptStream) cryptText = cryptStream.string return(cryptText) end
Private Instance Methods
block_size()
click to toggle source
# File lib/bicrypt.rb, line 205 def block_size 8 end
carefully_open_file(filename, mode)
click to toggle source
# File lib/bicrypt.rb, line 273 def carefully_open_file(filename, mode) begin aFile = File.new(filename, mode) rescue puts "Sorry. There was a problem opening the file <#{filename}>." aFile.close() unless aFile.nil? raise end return(aFile) end
decrypt_block(block)
click to toggle source
# File lib/bicrypt.rb, line 197 def decrypt_block(block) xl, xr = block.unpack('NN') xl, xr = decrypt_pair(xl, xr) decrypted = [xl, xr].pack('NN') return(decrypted) end
decrypt_pair(xl, xr)
click to toggle source
# File lib/bicrypt.rb, line 233 def decrypt_pair(xl, xr) xr ^= f(xl+@key[0]) xl ^= f(xr+@key[1]) xr ^= f(xl+@key[2]) xl ^= f(xr+@key[3]) xr ^= f(xl+@key[4]) xl ^= f(xr+@key[5]) xr ^= f(xl+@key[6]) xl ^= f(xr+@key[7]) 3.times { xr ^= f(xl+@key[7]) xl ^= f(xr+@key[6]) xr ^= f(xl+@key[5]) xl ^= f(xr+@key[4]) xr ^= f(xl+@key[3]) xl ^= f(xr+@key[2]) xr ^= f(xl+@key[1]) xl ^= f(xr+@key[0]) } return([xr, xl]) end
encrypt_block(block)
click to toggle source
# File lib/bicrypt.rb, line 189 def encrypt_block(block) xl, xr = block.unpack('NN') xl, xr = encrypt_pair(xl, xr) encrypted = [xl, xr].pack('NN') return(encrypted) end
encrypt_pair(xl, xr)
click to toggle source
# File lib/bicrypt.rb, line 210 def encrypt_pair(xl, xr) 3.times { xr ^= f(xl+@key[0]) xl ^= f(xr+@key[1]) xr ^= f(xl+@key[2]) xl ^= f(xr+@key[3]) xr ^= f(xl+@key[4]) xl ^= f(xr+@key[5]) xr ^= f(xl+@key[6]) xl ^= f(xr+@key[7]) } xr ^= f(xl+@key[7]) xl ^= f(xr+@key[6]) xr ^= f(xl+@key[5]) xl ^= f(xr+@key[4]) xr ^= f(xl+@key[3]) xl ^= f(xr+@key[2]) xr ^= f(xl+@key[1]) xl ^= f(xr+@key[0]) return([xr, xl]) end
f(longWord)
click to toggle source
# File lib/bicrypt.rb, line 256 def f(longWord) longWord = longWord % ULONG a, b, c, d = [longWord].pack('L').unpack('CCCC') return(sTable[3][d] ^ sTable[2][c] ^ sTable[1][b] ^ sTable[0][a]) end
generate_initialization_vector(words)
click to toggle source
# File lib/bicrypt.rb, line 263 def generate_initialization_vector(words) srand(Time.now.to_i) vector = "" words.times { vector << [rand(ULONG)].pack('N') } return(vector) end
sTable()
click to toggle source
Calculated S-boxes
# File lib/bicrypt.rb, line 173 def sTable @sTable ||= ( sTable = [[], [], [], []] 0.upto(3) { |i| 0.upto(255) { |j| t = sBox[2*i][j % 16] | (sBox[2*i+1][j/16] << 4) u = (8*i + 11) % 32 v = (t << u) | (t >> (32-u)) sTable[i][j] = (v % ULONG) } } sTable ) end
xor(str1, str2)
click to toggle source
Binary XOR of two strings.
puts "\000\000\001\001" ^ "\000\001\000\001" puts "\003\003\003" ^ "\000\001\002"
produces
"\000\001\001\000" "\003\002\001"
# File lib/bicrypt.rb, line 298 def xor(str1, str2) a = str1.unpack('C'*(str1.length)) #.bytes.to_a b = str2.unpack('C'*(str2.length)) #.bytes.to_a if (b.length < a.length) (a.length - b.length).times { b << 0 } end xor = "" 0.upto(a.length - 1) do |pos| x = a[pos] ^ b[pos] xor << x.chr() end return(xor) end