domingo, 1 de enero de 2017

Cuidado con los bytes ocultos (variante Arc4 y base64)

Aquí una versión PHP de una variante de la encriptación ARC4 (Alleged-Rivest Cipher, parte de los protocolos de cifrado más comunes como WEP, WPA para tarjetas wireless y TLS).

function varc4($clave,$string)
                {
$x=strlen($clave);

for($h=0;$h<256;$h++)
                      {$b[$h]=$h;}

for($h=0;$h<256;$h++)
{
$f=($f+$b[$h]+ord($clave[$h%$x]))%256;

  //swap

$i=$b[$h];
$b[$h]=$b[$f];
$b[$f]=$i;
}

$f=$h=0;

for($q=0;$q<strlen($string);$q++)
{
$h=($h+1)%256;
$f=($f+$b[$h])%256;

//swap

$i=$b[$h];
$b[$h]=$b[$f];
$b[$f]=$i;

$d.=chr(ord($string[$q])^$b[($b[$h]+$b[$f])%256]);
                        }

return $d;
}

Supongamos que usamos la clave "becaf9be" y codificamos "348319812478410a334dd_1724"

pasándolo a base64

base64_encode(varc4("becaf9be","348319812478410a334dd_1724"));

NAXYTQwXLrrY1ffJ8eJzA4P3vUdp1wzMWhs=

El algoritmo es el mismo para encriptar y desencriptar, con lo que

varc4("becaf9be",base64_decode("NAXYTQwXLrrY1ffJ8eJzA4P3vUdp1wzMWhs="));

debería dar el valor de partida. Pero da 348319812478410a334

Problema: el algoritmo puede perder valores de la cadena, y el problema es la manera en que se calcula la longitud de $string cuando son valores binarios.

strlen(base64_decode("NAXYTQwXLrrY1ffJ8eJzA4P3vUdp1wzMWhs="))=19

pero la cadena debería ser de 26.

Solución: aplicar mb_strlen($string, "8bit")

varc4 es utilizado por páginas como Youku