Reputation: 840
I am trying to convert a message into a ASCII hex value string and back again. However, I am having a lot of trouble with my ^ bit wise XOR operator. I've spent the last 4 hours searching stackoverflow's similar questions on XOR and bit wise manipulation, but no suggestion I've seen has fixed this issue yet.
I have a RakeTest file, which the following made up test:
def test_xor
key = 'hi'
msg = 'Hello There, how are you?'
key_trunc = key
key_trunc << key while key.length < msg.length
key_trunc = Decrypt.truncate_key key_trunc, msg.length
ct = Decrypt.xor msg, key_trunc
assert_equal('200c040507493c010d1b0d454801071e48081a0c4810071c57', ct)
end
I've worked out by hand (and verified with an online hex converter) that the correct ASCII hex result should be what is above. Here is my Decrypt module:
module Decrypt
# Returns an array of ASCII Hex values
def self.to_hex_array(str_hex)
raise ArgumentError 'Argument is not a string.' unless str_hex.is_a? String
result = str_hex.unpack('C*').map { |e| e.to_s 16 }
result
end
def self.to_str_from_hex_array(hex_str)
return [hex_str].pack 'H*'
end
def self.xor(msg, key)
msg = self.to_hex_array msg
key = self.to_hex_array key
xor = []
(0...msg.length).each do |i|
xor.push msg[i] ^ key[i]
end
return xor.join
end
def self.truncate_key(str, len)
str = str.to_s[0...len] if str.length > len
return str
end
end
I've confirmed in two separate rake test functions that to_hex_array
and to_str_from_hex_array
are working correctly. When I run the above rake test, I get a 'NoMethodError: undefined method '^' for "48":String
. 48 is the beginning hex value, and obviously Strings cannot undergo bit wise operations, but I have tried every method I could find to convert the values such that '^' will operate correctly.
The closest I can get (no error thrown) is to change the operation inside the loop to msg[i].hex ^ key[i].hex
, but that outputs an ASCII dec value.Can anyone help me out?
EDIT: Thanks to the suggestions below, I am able to run the following tests successfully:
def test_xor
key = 'hi'
msg = 'Hello There, how are you?'
key_trunc = key
key_trunc << key while key.length < msg.length
key_trunc = Decrypt.truncate key_trunc, msg.length
ct = Decrypt.xor msg, key_trunc
assert_equal(['200c040507493c010d1b0d454801071e48081a0c4810071c57'], ct)
end
def test_decrypt
msg = 'attack at dawn'
key = '6c73d5240a948c86981bc294814d'
key = [key].pack('H*')
new_key = Decrypt.xor msg, key
assert_equal(['0d07a14569fface7ec3ba6f5f623'], new_key)
ct = Decrypt.xor 'attack at dusk', new_key.pack('H*')
assert_equal(['6c73d5240a948c86981bc2808548'], ct)
end
For those interested, here is the successful Decrypt module:
module Decrypt
# Returns an array of ASCII Hex values
def self.to_dec_array(str_hex)
raise ArgumentError, 'Argument is not a string!' unless str_hex.is_a? String
dec_array = str_hex.unpack('C*')
dec_array
end
def self.to_str_from_dec_array(dec_array)
raise ArgumentError, 'Argument is not an array!' unless dec_array.is_a? Array
return dec_array.pack 'C*'
end
def self.print_dec_array(dec_array)
return [dec]
end
def self.xor(msg, key)
msg = self.to_dec_array msg
key = self.to_dec_array key
xor = []
(0...msg.length).each do |i|
xor.push msg[i] ^ key[i]
end
xor = xor.pack('C*').unpack('H*')
xor
end
def self.truncate(str, len)
str = str.to_s[0...len] if str.length > len
return str
end
end
Upvotes: 2
Views: 928
Reputation: 84124
In your to_hex_array
method you shouldn't be converting the bytes to strings (your call to to_s 16
) - that's why you end up trying to xor strings rather than integers
That does mean that your xor
method will need an extra step to turn the result from an array of integers to a string - something like
array_of_integers.map {|int| int.to_s(16)}.join
Upvotes: 2