3

Update 2 - it works

Thanks to Tom Leek for his answer.

The CFB mode of mycrypt for Rijndael uses a 8-bit feedback loop and not a feedback loop the length of the key/iv.

You have to force it to use the full length with the parameter ncfb. Confusingly this is not a documented constant in the PHP Mcrypt library: http://www.php.net/manual/en/mcrypt.constants.php

The appropriate lines in the PHP now looks like this:

$Crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $Key, $Text, 'ncfb', $IV);

$Decrypt = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $Key, $Crypt, 'ncfb', $IV);

Updated - see bottom

I am trying to get and Erlang site to exchange information via aes_cfb_128 encryption with a PHP site. I am writing a test implementation which will demonstrate that the PHP will decrypt what the Erlang encrypts and vice-versa - but I am having problems.

Here is the PHP source (apologies it is a bit shoddy - my PHP is no good - I am an Erlang programmer):

<?php

function dump($String, $Bin) {
echo $String . " is " . ord($Bin[0]) . " " . ord($Bin[1]) . " " . ord($Bin[2]) . " " . ord($Bin[3]) . " " . ord($Bin[4]) . " " . ord($Bin[5]) . " " . ord($Bin[6]) . " " . ord($Bin[7]) . " " . ord($Bin[8]) . " " . ord($Bin[9]) . " " . ord($Bin[10]) . " " . ord($Bin[11]) . " " . ord($Bin[12]) . " " . ord($Bin[13]) . " " . ord($Bin[14]) . " " . ord($Bin[15]) . " " . ord($Bin[16]) . " " . ord($Bin[17]) . " " . ord($Bin[18]) . " " . ord($Bin[19]) . " " . ord($Bin[20]) . " " . ord($Bin[21]) . " " . ord($Bin[22]) . " " . ord($Bin[23]) . " " . ord($Bin[24]) . " " . ord($Bin[25]) . " " . ord($Bin[26]) . " " . ord($Bin[27]) . " " . ord($Bin[28]) . " " . ord($Bin[29]) . " " . ord($Bin[30]) . " " . ord($Bin[31]) ."\n";

}

$Key  = "abcdefghabcdefgh";
$IV   = "12345678abcdefgh";
$Text = "12345678123456781234567812345678";

$KeySize = strlen($Key) * 8;
$IVSize = strlen($IV) * 8;
$TextSize = strlen($Text) * 8;

$IVSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_NOFB);
echo "IVSze is " . $IVSize . " bytes or " . $IVSize * 8 . " bits\n";

$BlockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_NOFB);
echo "BlockSize is " . $BlockSize . " bytes or " . $IVSize * 8 . " bits\n";

$Crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $Key, $Text, MCRYPT_MODE_NOFB, $IV);

$Decrypt = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $Key, $Crypt, MCRYPT_MODE_NOFB, $IV);

echo "Key   is   " . $Key  . " with size " . $KeySize  . "\n";
echo "IV    is   " . $IV   . " with size " . $IVSize   . "\n";
echo "Text  is   " . $Text . " with size " . $TextSize . "\n";
echo "Crypt is " . $Crypt  . "\n";
dump("Crypt", $Crypt);
echo "Decrypt is " . $Decrypt . "\n";
dump("Decrypt", $Decrypt);
?>

Here is the corresponding Erlang source:

-module(test_crypto).

-export([
         test/0
        ]).

test() ->
    Key  = <<"abcdefghabcdefgh">>,
    IV   = <<"12345678abcdefgh">>,
    Text = <<"12345678123456781234567812345678">> ,

    KeySize  = bit_size(Key),
    IVSize   = bit_size(IV),
    TextSize = bit_size(Text),

    io:format("Key  is ~p with size ~p~n", [Key, KeySize]),
    io:format("IV   is ~p with size ~p~n", [IV, IVSize]),
    io:format("Text is ~p with size ~p~n", [Text, TextSize]),

    Crypt = crypto:aes_cfb_128_encrypt(Key, IV, Text),
    io:format("Crypt is ~p~n", [Crypt]),
    B64 = base64:encode(Crypt),
    io:format("Crypt B64 is ~p~n", [B64]),

    Decrypt = crypto:aes_cfb_128_decrypt(Key, IV, Crypt),
    io:format("Decrypt is ~p~n", [Decrypt]),
    ok.

Here is the PHP output:

gordon@hypernumbers:~/crypto$ php test_crypto.php
IVSze is 16 bytes or 128 bits
BlockSize is 16 bytes or 128 bits
Key   is   abcdefghabcdefgh with size 128
IV    is   12345678abcdefgh with size 16
Text  is   12345678123456781234567812345678 with size 256
Crypt is ��^DЭZ�!�)�y�9�������ht���'!
Crypt is 139 182 94 68 208 173 127 90 14 236 33 230 41 29 210 121 153 57 173 191 237 169 242 222 217 104 116 144 240 175 39 33
Decrypt is 12345678123456781234567812345678
Decrypt is 49 50 51 52 53 54 55 56 49 50 51 52 53 54 55 56 49 50 51 52 53 54 55 56 49 50 51 52 53 54 55 56

Here is the Erlang output:

7> test_crypto:test().
Key  is <<"abcdefghabcdefgh">> with size 128
IV   is <<"12345678abcdefgh">> with size 128
Text is <<"12345678123456781234567812345678">> with size 256
Crypt is <<139,182,94,68,208,173,127,90,14,236,33,230,41,29,210,121,147,172,
           114,74,61,11,162,5,112,104,102,63,24,78,34,179>>
Crypt B64 is <<"i7ZeRNCtf1oO7CHmKR3SeZOscko9C6IFcGhmPxhOIrM=">>
Decrypt is <<"12345678123456781234567812345678">>
ok

As you can see both Code Blocks cycle themselves correctly - but they will not work together because they do not create identical cyphertext.

The first 16 bytes of the cypertext are the same - but the second block of 16 bytes is different.

I know this algorithm uses the first output block in the encryption of the second one - so I suspect there is some jiggery-pokery going on between how the Erlang crypto module is doing it and the PHP mcrypt code.

UPDATE

I tried using CFB mode in mycrypt but I believe that there is a different feedback blocksize or something - and only the first character of the cypertext was the same. NOFB mode seems to be related somehow and is the best I have got so far...

If I run the PHP with CFB mode I get this output:

~/crypto$ php test_crypto.php
IVSze is 16 bytes or 128 bits
BlockSize is 16 bytes or 128 bits
Key   is   abcdefghabcdefgh with size 128
IV    is   12345678abcdefgh with size 16
Text  is   12345678123456781234567812345678 with size 256
Crypt is ��*�b�ls�M����
                           ��ҮF��Y�=O
Crypt is 139 0 188 42 175 98 18 177 108 27 115 189 77 144 127 176 171 193 11 32 245 139 210 174 70 151 230 89 169 22 61 79
Decrypt is 12345678123456781234567812345678
Decrypt is 49 50 51 52 53 54 55 56 49 50 51 52 53 54 55 56 49 50 51 52 53 54 55 56 49 50 51 52 53 54 55 56

I tried getting help on StackOverflow https://stackoverflow.com/questions/14550195/crypto-between-erlang-and-php but it was mostly me talking to myself :(

1 Answer 1

3

Your PHP code uses MCRYPT_MODE_NOFB when encrypting and decrypting; you use the correct constant MCRYPT_MODE_CFB when you get the IV and block sizes, but after you switch to the other constant. As explained on this page, MCRYPT_MODE_NOFB does not implement CFB, but another mode called OFB. Hence the discrepancy.

Update: both CFB and OFB modes have a "feedback length". When they use a feedback of n bits (from 1 to the block size, i.e. 128 with AES), they encrypt n bits of data per invocation of the underlying block cipher. Using a feedback length smaller than the block size thus makes the encryption slower, so nobody does that.

Historically, small feedback lengths were meant to help with transport mediums which can lose both bits and synchronization -- the kind of description which applies to Samuel Morse's telegraph. With 1-bit feedback, decryption can "recover" after a few bits, thus limiting the impact of the transmission error. This property is quite useless on any electronic device which has been invented since the days of Elvis Presley.

Inexplicably (but typically), PHP designers decided to use OFB with 8-bit feedback, reserving the alternate constant (NOFB) for full block feedback. It is possible that they did the same kind of weird choice for CFB.

Summary: for both CFB and OFB (which are distinct from each other and cannot be used interchangeably), you have to worry about the "feedback length" which is not necessarily documented with high clarity in various crypto libraries. Both encryption and decryption must use the same feedback length to interoperate.

4
  • Apologies I realised that I had it a bit wrong - I have added an extra bit... Commented Feb 2, 2013 at 19:25
  • ie I have updated the question with a section on CFB mode. Commented Feb 2, 2013 at 19:28
  • Yay, works - see update... Commented Feb 2, 2013 at 20:05
  • I have posted a link to this question on PHP documentation so hopefully the next person using Erlang (and others) won't get caught by this... Commented Feb 2, 2013 at 20:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .