Search code examples
rubybit-manipulationxorruby-1.9.2

Simple XOR ruby 1.9.2


Apparently this used to work on ruby 1.8.7 but unfortunately not on 1.9.2

class String
  def xor(key)
    text = dup
    text.length.times {|n| text[n] ^= key[n.modulo key.size] }
    text
  end
end

def encode(_original, _pass = 'testvendor')
  _original.xor(_pass)
end

puts encode('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.')

#output=>    
8
EE
DEBDREBDEVSR
TTTT
TNZV0D
SE E    CRVSETENR   D

TT
    EKS0DSO VD
EVVTE S 
RSREXE+E T
 RR
T _TOEDE RO E
TTD
K

It returns

NoMethodError: undefined method `^' for "V":String

Any idea on how to get this working?

Thanks a lot


Solution

  • In 1.8, the String#[] method returned a Fixnum which was the byte at the specified index. In newer version, String#[] returns a String because strings are made of characters and the character-to-byte mapping depends on the encoding. Looks like you're using a String as a byte buffer so you should be working in Array instead of String:

    class Array
      def xor(key)
         a = dup
         a.length.times { |n| a[n] ^= key[n % key.size] }
         a
      end
    end
    

    And then to use it:

    mangled_array = string.codepoints.to_a.xor(key.codepoints.to_a)
    

    Then if you really want a String (which will contain a bunch of unprintable control characters and zero bytes and such things), then:

    mangled_string = mangled_array.inject('') { |s,c| s << c }
    

    And then to unpack:

    mangled_string.
      codepoints.
      to_a.
      xor(key.codepoints.to_a).
      inject('') { |s,c| s << c }
    

    All of this should maintain UTF-8 all the way through and that's what you want.

    You could probably patch your xor into Enumerable and skip the to_a business if desired. You could probably also adapt this to patch for String as well.

    You shouldn't be using String for byte buffers anymore, you're better off using arrays of Fixnum for that with explicit encoding handling.