El mismo código de salazón de cadenas da resultados diferentes

I have a class in both PHP and C++ to simply take a value string and salt string to do some salting for privacy. The idea is a PHP script will encrypt a string for a C++ program to receive and decrypt. They use a pre-shared salt string, synchronous mode.

The issue is with what appears to be the same logic they generate a different result for encrypting the same string. This means that decrypting the string either end will not result in the original string it was given.

It is probably something really simple I have missed or made a mistake on. Or possibly it is related to PHP using character encoding where as C++ is a raw bit stream. The PHP script is set to use a plain text output, with 'us-ascii' encoding.

Here is the PHP class:

define( 'NUM_STRINGS', 256 );

class CTwEncryption
{
    function Crypt( $szValue, $szSalt )
    {
        $iValueSize = (int)strlen( $szValue );
        $iSaltSize  = (int)strlen( $szSalt );

        $szStrings  = array();
        $szKeys     = array();

        $j = 1;

        // Init array of 0-255
        for ( $i = 0; $i < NUM_STRINGS; $i++ )
            $szStrings[ $i ] = $i;

        // Init array of 0-255 with a calculated char value
        for ( $i = 0; $i < NUM_STRINGS; $i++ )
        {
            if ( $j > $iSaltSize )
                $j = 1;

            $szKeys[ $i ] = ord( substr( $szSalt, $j, 1 ) );
            $j++;
        }

        // Shuffle the array values around to give a random value
        $j = 0;
        for ( $i = 0; $i < NUM_STRINGS; $i++ )
        {
            $j = ( $j + $szStrings[ $i ] + $szKeys[ $i ] ) % NUM_STRINGS;

            $szTemp = $szStrings[ $i ];
            $szStrings[ $i ] = $szStrings[ $j ];
            $szStrings[ $j ] = $szTemp;
        }

        // Encrypt/decrypt the string
        $szReturnValue = null;
        $i = 0;
        $j = 0;

        for ( $x = 0; $x < $iValueSize; $x++ )
        {
            $i = ( $i + 1 ) % NUM_STRINGS;
            $j = ( $j + $szStrings[ $i ] ) % NUM_STRINGS;

            $szTemp = $szStrings[ $i ];
            $szStrings[ $i ] = $szStrings[ $j ];
            $szStrings[ $j ] = $szTemp;

            $t = ( $szStrings[ $i ] + ( $szStrings[ $j ] % NUM_STRINGS ) ) % NUM_STRINGS;
            $y = $szStrings[ $t ];

            $cCrypt = chr( substr( $szValue, $x, 1 ) ^ $y );
            $szReturnValue .= $cCrypt;
        }

        // Return encrypted/decrypted string
        return $szReturnValue;
    }
}

Here is the C++ class:

#define NUM_STRINGS 256

class CTwEncryption
{
private:
    char    *szWorking;

public:
    CTwEncryption()     { szWorking = NULL; };
    ~CTwEncryption()    { if ( szWorking != NULL ) { delete szWorking; szWorking = NULL; } };

    char *Crypt( const char szValue[], const char szSalt[] )
    {
        const int iValueSize = (int)strlen( szValue );
        const int iSaltSize = (int)strlen( szSalt );

        if ( iValueSize == 0 || iSaltSize == 0 )
            return NULL;

        int j = 1;

        char *szStrings[ NUM_STRINGS ];
        char *szKeys[ NUM_STRINGS ];

        // Init array of 0-255
        for ( int i = 0; i < NUM_STRINGS; i++ )
        {
            char *szString = new char[ iValueSize + 1 ];

            itoa( i, szString, 10 );
            szString[ iValueSize ] = 0;

            szStrings[ i ] = szString;
        }

        // Init array of 0-255 with a calculated char value
        for ( int i = 0; i < NUM_STRINGS; i++ )
        {
            char *szKey = new char[ iValueSize + 1 ];

            if ( j > iSaltSize )
                j = 1;

            itoa( (int)( szSalt[ j ] ), szKey, 10 );
            szKey[ iValueSize ] = 0;

            szKeys[ i ] = szKey;
            j++;
        }

        // Shuffle the array values around to give a random value
        j = 0;
        for ( int i = 0; i < NUM_STRINGS; i++ )
        {
            j = ( j + atoi( szStrings[ i ] ) + atoi( szKeys[ i ] ) ) % NUM_STRINGS;

            char *szTemp = szStrings[ i ];
            szStrings[ i ] = szStrings[ j ];
            szStrings[ j ] = szTemp;
        }

        // Encrypt/decrypt the string
        szWorking = new char[ iValueSize + 1 ];
        for ( int i = 0; i <= iValueSize; i++ )
            szWorking[ i ] = 0;

        int i = 0;
        j = 0;

        for ( int x = 0; x <= iValueSize; x++ )
        {
            i = ( i + 1 ) % NUM_STRINGS;
            j = ( j + atoi( szStrings[ i ] ) ) % NUM_STRINGS;

            char *szTemp = szStrings[ i ];
            szStrings[ i ] = szStrings[ j ];
            szStrings[ j ] = szTemp;

            int t = ( atoi( szStrings[ i ] ) + ( atoi( szStrings[ j ] ) % NUM_STRINGS ) ) % NUM_STRINGS;
            int y = atoi( szStrings[ t ] );

            char cCrypt = char( (int)( szValue[ x ] ) ^ y );
            szWorking[ x ] = cCrypt;
        }

        // Clean dynamic memory
        for ( int i = 0; i < NUM_STRINGS; i++ )
        {
            delete szStrings[ i ];
            delete szKeys[ i ];

            szStrings[ i ] = NULL;
            szKeys[ i ] = NULL;
        }

        // Return encrypted/decrypted string
        szWorking[ iValueSize ] = 0;
        return szWorking;
    }
};

Any help here would be appreciated, thanks :)

preguntado el 03 de mayo de 12 a las 12:05

StackOverflow no es un servicio de depuración gratuito. -

What debugging have you done? Have you confirmed that all intermediate variables have equivalent values in PHP and C++? -

¿Ha considerado usar algo como phpseclib for pure-PHP implementations of standard encryption techniques, for which many other languages have built in support? It's a lot simpler (and probably more efficient) than trying to roll your own... -

2 Respuestas

I'm not sure but maybe using mb_* functions could help:

  • en lugar de strlen utilizado mb_strlen
  • en lugar de substr utilizado mb_substr

Either give it just the value or also the encoding (but each mb_* function should check for the string encoding if no one is provided).

contestado el 03 de mayo de 12 a las 13:05

Ah nice idea there. Installed the mbstring extension and used those, though the PHP script produced the same result. - adánbean

@Adambean Then, as somebody already mentioned above, You should do the step-by-step (variable-by-variable) debugging of both scripts y ver where the difference first occurs and why. - sombreado

Figured this out. Looks like I need to send input to a PHP script via a HTTP PUT request, and read it using fopen( "php://input", "rb" ). It appeared that PHP was not hanlding anything in a binary safe fashion. Also both on C++ and PHP I'm treating each character as an integer, which should allow UTF-32 strings to be handled correctly in binary safe mode.

Here is my C++ class, I have mine in "twencrypt.h":

#ifndef TWCRYPT_H
#define TWCRYPT_H

/***
*
*   Two-way string encryption
*   This will encrypt/decrypt a string using a salt.
*
*   -AdamR
*
****/

#define NUM_STRINGS 256

class CTwEncryption
{
private:
    char    *szWorking;

public:
    CTwEncryption()     { szWorking = NULL; };
    ~CTwEncryption()    { if ( szWorking != NULL ) { delete szWorking; szWorking = NULL; } };

    char *Crypt( const char szValue[], const char szSalt[] )
    {
        const int iValueSize = (int)strlen( szValue );
        const int iSaltSize = (int)strlen( szSalt );

        if ( iValueSize < 1 || iSaltSize <  1 )
            return NULL;

        int j = 1;

        int iChars[ NUM_STRINGS ];
        int iKeys[ NUM_STRINGS ];

        // Init array of 0-255
        for ( int i = 0; i < NUM_STRINGS; i++ )
            iChars[ i ] = i;

        // Init array of 0-255 with a calculated char value
        for ( int i = 0; i < NUM_STRINGS; i++ )
        {
            if ( j > iSaltSize )
                j = 1;

            iKeys[ i ] = szSalt[ j ];
            j++;
        }

        // Shuffle the array values around to give a random value
        j = 0;
        for ( int i = 0; i < NUM_STRINGS; i++ )
        {
            j = ( j + iChars[ i ] + iKeys[ i ] ) % NUM_STRINGS;

            int iTemp = iChars[ i ];
            iChars[ i ] = iChars[ j ];
            iChars[ j ] = iTemp;
        }

        // Encrypt/decrypt the string
        szWorking = new char[ iValueSize + 1 ];
        for ( int i = 0; i <= iValueSize; i++ )
            szWorking[ i ] = 0;

        int i = 0;
        j = 0;

        for ( int x = 0; x <= iValueSize; x++ )
        {
            i = ( i + 1 ) % NUM_STRINGS;
            j = ( j + iChars[ i ] ) % NUM_STRINGS;

            int iTemp = iChars[ i ];
            iChars[ i ] = iChars[ j ];
            iChars[ j ] = iTemp;

            int t = ( iChars[ i ] + ( iChars[ j ] % NUM_STRINGS ) ) % NUM_STRINGS;
            int y = iChars[ t ];

            char cCrypt = char( (int)( szValue[ x ] ) ^ y );
            szWorking[ x ] = cCrypt;
        }

        // Return encrypted/decrypted string
        szWorking[ iValueSize ] = 0;
        return szWorking;
    }
};

#endif

As it returns a char pointer I recommend you use strcpy() to put it in a safe place. Here is an example, and bare in mind the exact same code is used to decrypt a string too.

const char *szString = "My string to encrypt";
const char *szSalt   = "Some salt here :D";
int iStringSize      = (int)strlen( szString );

char *szEncrypted = new char( iStringSize ) + 1 );
CTwEncryption *pTwCrypt = new CTwEncryption();
strcpy( szEncrypted, pTwCrypt->Crypt( szString, szSalt );
szEncrypted[ iStringSize ] = 0;
delete pTwCrypt;

Here is my PHP class:

<?php
    define( 'NUM_STRINGS', 256 );

    class CTwEncryption
    {
        function Crypt( $szValue, $szSalt )
        {
            $iValueSize = strlen( $szValue );
            $iSaltSize  = strlen( $szSalt );

            if ( $iValueSize == 0 || $iSaltSize == 0 )
                return null;

            $j = 1;

            $iChars = array();
            $iKeys  = array();

            // Init array of 0-255
            for ( $i = 0; $i < NUM_STRINGS; $i++ )
                $iChars[ $i ] = $i;

            // Init array of 0-255 with a calculated char value
            for ( $i = 0; $i < NUM_STRINGS; $i++ )
            {
                if ( $j > $iSaltSize )
                    $j = 1;

                $iKeys[ $i ] = ord( $szSalt[ $j ] );
                $j++;
            }

            // Shuffle the array values around to give a random value
            $j = 0;
            for ( $i = 0; $i < NUM_STRINGS; $i++ )
            {
                $j = ( $j + $iChars[ $i ] + $iKeys[ $i ] ) % NUM_STRINGS;

                $iTemp = $iChars[ $i ];
                $iChars[ $i ] = $iChars[ $j ];
                $iChars[ $j ] = $iTemp;
            }

            // Encrypt/decrypt the string
            $szReturnValue = null;
            $i = 0;
            $j = 0;

            for ( $x = 0; $x < $iValueSize; $x++ )
            {
                $i = ( $i + 1 ) % NUM_STRINGS;
                $j = ( $j + $iChars[ $i ] ) % NUM_STRINGS;

                $iTemp = $iChars[ $i ];
                $iChars[ $i ] = $iChars[ $j ];
                $iChars[ $j ] = $iTemp;

                $t = ( $iChars[ $i ] + ( $iChars[ $j ] % NUM_STRINGS ) ) % NUM_STRINGS;
                $y = $iChars[ $t ];

                $iValue = str_split( $szValue );
                for ( $c = 0; $c < $iValueSize; $c++ )
                    $iValue[ $c ] = ord( $iValue[ $c ] );

                $cCrypt = chr( $iValue[ $x ] ^ $y );
                $szReturnValue .= $cCrypt;
            }

            // Return encrypted/decrypted string
            return $szReturnValue;
        }
    }

    $c_TwEncryption = new CTwEncryption;
?>

This one is a bit easier to use. It's simply:

$szString    = "My string to hide lollercoaster";
$szSalt      = "super duper password of doom";

$szEncrypted = $c_TwEncryption->Crypt( $szString, $szSalt );
$szDecrypted = $c_TwEncryption->Crypt( $szEncrypted, $szSalt );

Remember that you should not define $szString or $szSalt (PHP side) via a HTTP GET or POST request. Be safe and use a PUT request, and read it like this:

$szString = null;
$hInData = fopen( "php://input", "rb" ) || die( "Unable to open HTTP PUT handle." );

if( $hInData != null )
{
    while ( $bData = fread( $hRequest, 1024 ) )
        $szString .= $bData;
}
else
    die( "Unable to read HTTP PUT data." );

fClose( $hInData ) || die( "Unable to close HTTP PUT handle." );

if( $szString == null || empty( $szString ) )
    die( "No data read from HTTP PUT stream." );

¡A disfrutar!

Respondido el 26 de junio de 12 a las 21:06

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.