Search code examples
phprubycode-translation

Encryption Algorithm from PHP to Ruby (Vignere variant)


I am a bit stuck with this. I have to interface with an api that uses a version of an encryption algorithm that they seem to have ripped from Typo3 written by Ari Kuorikoski.

I need to create a ruby lib to interface with their api, so have to port their algorithm into ruby, and I am a bit out of my depth when it comes to encryption.

This is the code:

private function keyED($txt) { 
$encrypt_key = md5($this->encrypt_key); 
$ctr=0; 
$tmp = ""; 
for ($i=0;$i<strlen($txt);$i++) { 
   if ($ctr==strlen($encrypt_key)) $ctr=0; 
   $tmp.= substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1); 
   $ctr++; 
} 
return $tmp; 
} 

private function encrypt($txt){ 
srand((double)microtime()*1000000); 
$encrypt_key = md5(rand(0,32000)); 
$ctr=0; 
$tmp = ""; 
for ($i=0;$i<strlen($txt);$i++){ 
   if ($ctr==strlen($encrypt_key)) $ctr=0; 
   $tmp.= substr($encrypt_key,$ctr,1) . 
       (substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1)); 
   $ctr++; 
} 
return base64_encode($this->keyED($tmp)); 
}

The part that has me stumped is this, I have to write it for ruby 1.8.6 as that's the server that it will be on. And there's no XOR for strings. Not that I would understand it if there was.

Any help, pointers, ideas would be much much appreciated.

Addendum:

I realize, I didn't put any code up, the only difficulty is actually the xor problem, but here is my code so far:

def xor(s1,s2)
        if s2.empty? then
            return s1
        else
            a1 = s1.unpack("c*")
            a2 = s2.unpack("c*")
            a2 *= 2 while a2.length < a1.length
            return a1.zip(a2).collect {|c1,c2| c1 ^ c2}.pack("c*")
        end
    end
    def keyED(str)
        encrypt_key = Digest::MD5.digest(@key)
        ctr = 0
        tmp = ''
        for i in 0...str.length do
             ctr = 0 if ctr == encrypt_key.length
             tmp << xor(str.slice(i,1), encrypt_key.slice(ctr,1)).to_s
            ctr = ctr + 1
        end
        return tmp
    end

    # === Ported Code
    # This code was ported from Ari's Typo 3 Session Encryption
    def encrypt(str)
        encrypt_key = Digest::MD5.digest(rand(32000).to_s)
        ctr = 0
        tmp = ''
        for i in 0...str.length do
            ctr=0 if ctr==encrypt_key.length 
            tmp << encrypt_key.slice(ctr,1) << xor(str.slice(i,1), encrypt_key.slice(ctr,1))
            ctr = ctr + 1
        end
        return Base64.encode64(keyED(tmp))
    end
    def decrypt(str)
        txt = keyED(str)
        tmp = ''
        for i in 0...txt.length do 
            md = txt.slice(i,1)
            i = i + 1
            tmp << xor(txt.slice(i,1),md)
        end
        puts "Decrypte string:#{Base64.decode64(tmp)}EOSTRING"
    end

Solution

  • From Erik Veenstra:

    class String
      def xor(other)
        if other.empty?
          self
        else
          a1        = self.unpack("c*")
          a2        = other.unpack("c*")
          a2 *= 2   while a2.length < a1.length
          a1.zip(a2).collect{|c1,c2| c1^c2}.pack("c*")
        end
      end
    end
    

    Then your code becomes

    tmp << str.slice(i,1).xor(encrypt_key.slice(i,1))
    

    An alternate implementation of String#xor, as suggested by jolierouge and David Garamond:

    class String
      def xor(other)
        raise ArgumentError, "Can't bitwise-XOR a String with a non-String" unless other.kind_of? String
        raise ArgumentError, "Can't bitwise-XOR strings of different length" unless self.length == other.length
        (0..self.length-1).collect { |i| self[i] ^ other[i] }.pack("C*")
      end
    end