views:

813

answers:

2

Hi,

I have a XML file on a server encrypted with the RC4 algorithm (http://rc4crypt.devhome.org)

function encrypt ($pwd, $data, $ispwdHex = 0)
    {
        if ($ispwdHex)
            $pwd = @pack('H*', $pwd); // valid input, please!

        $key[] = '';
        $box[] = '';
        $cipher = '';

        $pwd_length = strlen($pwd);
        $data_length = strlen($data);

        for ($i = 0; $i < 256; $i++)
        {
            $key[$i] = ord($pwd[$i % $pwd_length]);
            $box[$i] = $i;
        }
        for ($j = $i = 0; $i < 256; $i++)
        {
            $j = ($j + $box[$i] + $key[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }
        for ($a = $j = $i = 0; $i < $data_length; $i++)
        {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $k = $box[(($box[$a] + $box[$j]) % 256)];
            $cipher .= chr(ord($data[$i]) ^ $k);
        }
        return $cipher;
    }

Here is the objective-C code I use to decrypt :

      NSData *dataToDecrypt = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.url.com/fileCrypted.xml"]] returningResponse:nil error:nil];
        const void *vplainText;
        size_t plainTextBufferSize;


            plainTextBufferSize = [dataToDecrypt length];
            vplainText = [dataToDecrypt bytes];


        CCCryptorStatus ccStatus;
        uint8_t *bufferPtr = NULL;
        size_t bufferPtrSize = 0;
        size_t movedBytes = 0;

        bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
        bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
        memset((void *)bufferPtr, 0x0, bufferPtrSize);

        NSString *key = @"mykey";
        //NSString *initVec = @"init Vec";
        const void *vkey = (const void *) [key UTF8String];
        const void *vinitVec = (const void *) [initVec UTF8String];

size_t keyLength = [[key dataUsingEncoding:NSUTF8StringEncoding] length]; 
        ccStatus = CCCrypt(kCCDecrypt,
                           kCCAlgorithmRC4,
                           0,
                           vkey,
                           kCCKeySizeDES,
                           nil,
                           vplainText,
                           plainTextBufferSize,
                           (void *)bufferPtr,
                           bufferPtrSize,
                           &movedBytes);
        if (ccStatus == kCCSuccess) NSLog(@"SUCCESS");
        /*else*/ if (ccStatus == kCCParamError) return @"PARAM ERROR";
        else if (ccStatus == kCCBufferTooSmall) return @"BUFFER TOO SMALL";
        else if (ccStatus == kCCMemoryFailure) return @"MEMORY FAILURE";
        else if (ccStatus == kCCAlignmentError) return @"ALIGNMENT";
        else if (ccStatus == kCCDecodeError) return @"DECODE ERROR";
        else if (ccStatus == kCCUnimplemented) return @"UNIMPLEMENTED";

        NSString *result = [[ NSString alloc ] initWithData: [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes] encoding:NSASCIIEncoding];

Logs output : SUCCESS but my result is not good (I tested many encoding but ASCII seems to be the good, cf. PHP function ord ...)

Are my both RC4 implementation standard ?

Edit: removed IV in Objective-C code Edit2: Objective-C KeyLength = password data length, removed option

+2  A: 

If you don't know what you're doing in cryptography, then "rolling-your-own" is a sure fire receipe for disaster. You don't have to believe me, read John Viega on The Cult of Schneier about general programmers who try to "roll their own" cryptography. Paraphrased: Don't do it.

Since this is a file, and I'm guessing a reasonable small file, you can use more standard and higher level libraries using standards such as SSL (OpenSSL and others) or OpenPGP (GPG, etc.) to do the necessary encryption / decryption. I believe there is library or module support for SSL in both Objective-C (or the iPhone environment) and PHP.

In particular about RC4, it is a stream cipher that is deceptively simple to write, but incredibility easy to mess up in its implementation details. See The Misuse of RC4 in Microsoft Word and Excel, and Can you recommend RC4 128-bit encrypted software? for an well known historic example, and a recommendation of security / cryptography expert (former CTO and co-founder of PGP Corp.).


Added:

The keysize in the php script is based on the raw length of the password data. It also does not use any PKCS7 Padding, so I believe that field should be zero (0) (CCryptor doesn't support padding of stream ciphers, and the php version certainly doesn't use it). In your code CCCrypt used a keysize of 8 bytes (64* bits), whereas I believe you want it to be the length (in bytes) of the password (binary data).

There is no MAC or hash of the data so the functions won't be able to determine valid from invalid decodings.

I think this will get you closer to compatibility with this insecure implementation of RC4 (RC4crypt in PHP).

mctylr
Hi, first of all, thanks for taking the time to advice me, and I think you're alright.But the matter is : I don't have any choice, the content provider is encrypting the file with RC4 crypt in PHP, and give me the key.I can't make him change the encryption for me because others services use the file. I don't think this is a good way, I usually advice to use an API system with a public / private key with a time variable.So I know what I'm doing is not the good way. But I must decode the file on the iPhone this way :-/All the best.
F.Santoni
I understand now.
mctylr
Hi mctylr,Thanks for helping me, I added the variable keyLength and removed the PKCS7 Padding option. ------ For valid or invalid decodings, if I get a valid XML, I pretty sure that the decoding is valid. ---- Can the error come from encoding ? I feel really stupid... :(
F.Santoni
If you mean can you introduce error from conversion of text encoding of data strings, then yes. I don't know Objective-C, so I am unclear about your usage of UTF-8 strings (`initVec UTF8String`), and then in the last line *encoding:NSASCIIEncoding*. The encryption and decryption treats both the input and output as a binary data, so working with an array of C-style `char` would be best if possible.
mctylr
BTW, here are some test vectors you can use for testing both implementations http://en.wikipedia.org/wiki/RC4#Test_vectors
mctylr
+1  A: 

Building on @mctylr, Avoid rolling your own whenever possible. If you need RC4, use a pre-built/tested library: OpenSSL RC4. You can replace your entire code with about 3 lines.

Harley Green