Question

I've the task to convert a crypt function someone made in perl into php code. Everything works okay except this:

Perl:

$wert = Encode::encode( "utf8", $wert );
$len=length $wert;
$pad = ($len % 16)?"0".chr(16 - ($len % 16)):"10";
$fuell = pack( "H*", $pad x (16 - $len % 16));

PHP:

$wert = utf8_encode($wert);
$len = mb_strlen($wert);
$pad = ( $len%16 ) ? '0'.chr(16 - ($len%16)) : '10';
$fuell = pack("H*", str_repeat($pad, (16 - $len % 16)));

The php version works okay for some strings. But when I have something like '2010-01-01T00:00:00.000' the perl version works without any error and the php version prints "PHP Warning: pack(): Type H: illegal hex digit".

I'm very grateful if someone can spot the error in the php version.

Edit:

This is the complete function I've to convert into php. It was made by a programmer of a company which doesn't work for us anymore so I can't really tell what the original intention was.

sub crypt
{
    my $self = shift;
    my ($wert,$pw)= @_;
    $wert = Encode::encode( "utf8", $wert );
    $pw = Encode::encode( "utf8", $pw );
    $len=length $wert;
    $pad = ($len % 16)?"0".chr(16 - ($len % 16)):"10";
    $fuell = pack( "H*", $pad  x (16 - $len % 16));
    $wert=$wert.$fuell;
    $lenpw=length $pw;
    $fuell = ($lenpw % 16)? pack ("H*", "00" x (16 - $lenpw % 16)):"";
    $pw=$pw.$fuell;
    $cipher = new Crypt::Rijndael $pw, Crypt::Rijndael::MODE_CBC;
    $cipher->set_iv($pw);
    $crypted = encode_base64($cipher->encrypt($wert),"");

    return $crypted;
}
Was it helpful?

Solution

It looks like the error is actually in both versions. The format code H looks for a hex digit, and as noted in the PHP error, it isn't finding (a legal) one. The culprit appears to be this expression:

chr(16 - ($len % 16))

The Perl version isn't complaining because Perl's version of pack will convert the character regardless of whether or not it is a hex digit (which may not be what you want). The documentation goes into more detail about what actually happens.

To prevent the error, try this instead:

sprintf('%x', 16 - ($len % 16))

Note: While this should fix the error you are getting, I don't know if it's an acceptable solution because I don't know the exact intent of the original author of the Perl code.

OTHER TIPS

It seems that the Perl implementation of pack() is tolerant of invalid hex digits in the input string, and the PHP version is decidedly not.

Consider:

print pack("H*", "ZZ");

This prints 3 in Perl (for some reason), but results in the error you mentioned in PHP.

I'm not sure exactly what Perl is doing with these 'digits', but it's definitely not the same as PHP.

EDIT: It looks like, Perl actually will "roll" the hex digit domain forward into the character set. That is:

0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ  #-- Give this to Perl...
0123456789ABCDEF0123456789ABCDEF0123  #-- .. and it's treated as this hex digit

Thus, "ZZ" is the same as "33", which is why it prints 3. Note that this behavior is not well-defined according to the documentation. Thus the original implementation in Perl can be considered buggy, since it relies on behavior that isn't well-defined.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top