1

I am using the Dynamic C programming language on a Rabbit microprocessor. I believe Dynamic C is based on ANSI-C89 with some very slight changes. They have an AES encryption library which I am trying to use to encrypt data before I send it to my server (with very little success).

Here's the documentation for the C-based AES functions that I'm using:

AESinitStream

SYNTAX: void AESinitStream(AESstreamState *state, const char *key, const char *init_vector);

DESCRIPTION: Sets up a stream state structure to begin encrypting or decrypting a stream. A particular stream state can only be used for one direction.

PARAMETER1: state - An AESstreamState structure to be initialized

PARAMETER2: key - the 16-byte cipher key, using a NULL pointer will prevent an existing key from being recalculated.

PARAMETER3: init_vector - a 16-byte array representing the initial state of the feedback registers. Both ends of the stream must begin with the same initialization vector and key.

AESencryptStream

SYNTAX: void AESencryptStream(AESstreamState *state, char *data, int count);

DESCRIPTION: Encrypts an array of bytes

PARAMETER1: state - The AESstreamState structure

PARAMETER2: data - an array of bytes that will be encrypted in place.

PARAMETER3: count - size of data array

Here's my C code:

    const char key[] = {'\x41', '\x41', '\x37', '\x44',
                        '\x44', '\x34', '\x30', '\x33',
                        '\x30', '\x35', '\x39', '\x4e',
                        '\x36', '\x37', '\x30', '\x38'};
    AESstreamState encrypt_state; //built in Dynamic C type
    char init_vector[16];
    int i;
    int bufLength;

    sprintf(Buf, "%s", "testabc");
    bufLength = strlen(Buf);

    for (i = 0; i < 16; i++)
        init_vector[i] = rand() % 255;

    printf("Key: ");
    for (i = 0; i < sizeof(key); i++)
        printf("%d ", key[i]);
    printf("\n");

    AESinitStream(&encrypt_state, key, init_vector);  //built in Dynamic C function
    AESencryptStream(&encrypt_state, Buf, bufLength); //built in Dynamic C function

    printf("Data: ");
    for (i = 0; i < strlen(Buf); i++)
        printf("%d ", Buf[i]);
    printf("\n");

    //set first byte to something that lets the server know it's encrypted
    //set 2nd through 16th byte to the IV (initialization vector) so every message will be different even if they have the same contents
    for (i = bufLength-1; i >= 0; i--)
        Buf[i+17] = Buf[i];
    Buf[0] = 237; //φ

    printf("IV: ");
    for (i = 1; i < 17; i++)
    {
        printf("%d ", init_vector[i-1]);
        Buf[i] = init_vector[i-1];
    }
    printf("\n");

Output:

Key: 65 65 55 68 68 52 48 51 48 53 57 78 54 55 48 56

Data: 249 78 60 83 130 167 37

IV: 74 121 108 165 83 120 36 27 161 192 84 76 105 239 34 214

Here's my C# code:

    private string DecryptAES(byte[] cipherText, byte[] IV)
    {
        byte[] key = {
            0x41, 0x41, 0x37, 0x44,
            0x44, 0x34, 0x30, 0x33,
            0x30, 0x35, 0x39, 0x4e,
            0x36, 0x37, 0x30, 0x38
        };

        // Check arguments.
        if (cipherText == null || cipherText.Length <= 0)
        {
            throw new ArgumentNullException("cipherText");
        }
        else if (IV == null || IV.Length <= 0 || IV.Length != 16)
        {
            throw new ArgumentNullException("IV");
        }

        Console.Write("Key: ");
        for (int i = 0; i < key.Length; i++)
            Console.Write("{0} ", key[i]);
        Console.WriteLine();

        Console.Write("Data: ");
        for (int i = 0; i < cipherText.Length; i++)
            Console.Write("{0} ", cipherText[i]);
        Console.WriteLine();

        Console.Write("IV: ");
        for (int i = 0; i < IV.Length; i++)
            Console.Write("{0} ", IV[i]);
        Console.WriteLine();

        // Create an RijndaelManaged object
        // with the specified key and IV.
        using (RijndaelManaged rijAlg = new RijndaelManaged())
        {
            rijAlg.Mode = CipherMode.CFB;
            rijAlg.FeedbackSize = 8;
            rijAlg.BlockSize = 128;
            rijAlg.Padding = PaddingMode.None;

            rijAlg.Key = key;
            rijAlg.IV = IV;

            // Create a decrytor to perform the stream transform.
            ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

            // Create the streams used for decryption.
            using (System.IO.MemoryStream msDecrypt = new System.IO.MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (System.IO.StreamReader srDecrypt = new System.IO.StreamReader(csDecrypt))
                    {
                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        string plaintext = null;

                        plaintext = srDecrypt.ReadToEnd();

                        Console.WriteLine("Decrypted: " + plaintext);

                        return plaintext;
                    }
                }

            }
        }
    }

Output:

Key: 65 65 55 68 68 52 48 51 48 53 57 78 54 55 48 56

Data: 249 78 60 83 130 167 37

IV: 74 121 108 165 83 120 36 27 161 192 84 76 105 239 34 214

Decrypted: t{^^?d2

As you can see, I'm getting some strange output in my decrypted text. Can anybody point me in the right direction?

I have tried decrypting this using Dynamic C successfully, but if you think the built in Dynamic C AES library is the culprit, what would you recommend?

EDIT: So I decided to encrypt this using C#'s AES libraries and got a different cipher:

    private byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
    {
        // Check arguments.
        if (plainText == null || plainText.Length <= 0)
        {
            throw new ArgumentNullException("plainText");
        }
        if (Key == null || Key.Length <= 0)
        {
            throw new ArgumentNullException("Key");
        }
        if (IV == null || IV.Length <= 0)
        {
            throw new ArgumentNullException("IV");
        }
        byte[] encrypted = null;
        // Create an RijndaelManaged object
        // with the specified key and IV.
        using (RijndaelManaged rijAlg = new RijndaelManaged())
        {
            rijAlg.Mode = CipherMode.CFB;
            rijAlg.FeedbackSize = 8;
            rijAlg.BlockSize = 128;
            rijAlg.Padding = PaddingMode.None;

            rijAlg.Key = Key;
            rijAlg.IV = IV;

            // Create a decrytor to perform the stream transform.
            ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
            // Create the streams used for encryption.
            using (System.IO.MemoryStream msEncrypt = new System.IO.MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (System.IO.StreamWriter swEncrypt = new System.IO.StreamWriter(csEncrypt))
                    {

                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }

        // Return the encrypted bytes from the memory stream.
        return encrypted;

    }

Calling the above function with the same string, key, & IV that I originally used results in the following cipher:

249 (which is correct), 43, 74, 118, 241, 179, 235

I changed the FeedbackSize to 16 and the first two bytes were correct, but it also gave me an error (Length of the data to encrypt is invalid.) and reduced the array size to 6 bytes. I'm clueless.

Here is a snippet of some sample code from Rabbit that uses AESencryptStream (they chose to set the IV to the same value as the key). It appears that I am using this function correctly but please tell me if I am wrong:

const char key[16] = {
   '\x06', '\xa9', '\x21', '\x40', '\x36', '\xb8', '\xa1', '\x5b',
   '\x51', '\x2e', '\x03', '\xd5', '\x34', '\x12', '\x00', '\x06'
};
char bblock[8192];
AESstreamState ass;
memset(bblock, 'A', sizeof(bblock));
AESinitStream(&ass, key, key);
AESencryptStream(&ass, bblock, sizeof(bblock));
AESinitStream(&ass, key, key);
AESdecryptStream(&ass, bblock, sizeof(bblock));
Ryan Foley
  • 193
  • 4
  • 11
  • Which chaining mode your C AES library is using? Also your ciphertext length doesn't make sense, as it should be multiple of a block length (16 bytes) – Eugene Sh. May 26 '16 at 16:50
  • Update: It looks like you are misusing the encryption functions as well.. Can't tell more as I don't have the documentation. But it doesn't make much sense to me what you are doing with the poor `Buf`.. – Eugene Sh. May 26 '16 at 16:55
  • I'm using CFB, which is not required to be a multiple of the block length (or at least I thought). I'll post some example code from the documentation later today :) – Ryan Foley May 26 '16 at 17:00
  • The ciphertext is *always* multiple of a blocksize in AES. – Eugene Sh. May 26 '16 at 17:01
  • @EugeneSh. I just had to test that, and it looks like if your FeedbackSize is set to 8 bits and you are using CFB, you are able to write cipher text that is not a multiple of your blocksize. And of course I can't fit the whole function in here... if you just change the C# function above to encrypt rather than decrypt you can test it for yourself though :) – Ryan Foley May 26 '16 at 17:45
  • I suggest you to read [this](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Feedback_.28CFB.29). Yes, CFB is self-synchronizing mode, which can recover from some data loss. But a correct ciphertext is always multiple of a block size. – Eugene Sh. May 26 '16 at 17:48
  • 1
    @EugeneSh. Encrypted data is **not** always a multiple of block size, there are several modes that do not produce a multiple of block size including CTR mode. – zaph May 26 '16 at 17:56
  • Essentially a counter value is encrypted and increments for each block. This output is Xor'ed with the data to be encrypted. If the data is shorter than a block only that portion is Xor'ed and output. Decryption is exactly the same, AES is run in encryption mode for both CTR encryption and decryption. See [CTR mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29). Now please delete the comments that AES must always produce a multiple of block size. – zaph May 26 '16 at 18:03
  • @zaph In CTR mode the *counter (+nonce)* is first encrypted, always producing 128 bit block. Then xored with the plain/ciphertext. Always producing another 128 bits. – Eugene Sh. May 26 '16 at 18:07
  • @EugeneSh. You are missing that while AES encryption will produce 16-bytes not all must be used, in fact those that are longer then the message are not used, the message does not need to be padded. – zaph May 26 '16 at 18:09
  • One of the advantages of CTR is that since no padding is not needed there is no chance or a padding oracle attack. – zaph May 26 '16 at 18:16
  • @zaph OK, I think I see what you mean. Since in CTR mode the AES-encrypted part (i.e. the counter) is known to all of the parties, there is no need to transmit it's encryption as it is not xored with anything other than padding (if exists). What about the OP's CFB mode? – Eugene Sh. May 26 '16 at 18:16
  • Look at the examples in [Block cipher mode of operation](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation), OFB and CFB are the same, the data is not run through the encryption, it is Xor-ed the output. GCM ode also does not se padding. – zaph May 26 '16 at 18:20
  • In this case I amend all of my comments regarding the CT length. Sorry for misleading. – Eugene Sh. May 26 '16 at 18:21
  • Thank you for clarifying that @zaph! @EugenSh., I found some sample code that I am adding to the question now. – Ryan Foley May 26 '16 at 19:26
  • Try a feedback length of 128. Also, is there some particular reason for using CFB mode? – zaph May 26 '16 at 19:55
  • @zaph, thanks for the suggestion! It almost works, but I either have to change "testingabc" to "testingabc " (with a bunch of spaces) to fill up the 16 bytes (or else VS tells me Length of the data to encrypt is invalid.), or set my PaddingMode to something else which will add some additional bytes onto the end of my cipher (these bytes do not appear in the cipher sent from the C code). The Rabbit libraries let you choose either CFB or CBC. I felt like CFB was a better fit for me since I'm sending a lot of smaller messages so the padding would have a pretty significant impact. – Ryan Foley May 26 '16 at 20:19
  • So it turns out the library does not do 8-bit (CFB8) so you will have to move to CBC. Is the padding really a performance issue? It would mean that you would have an extra few bytes to move. If that is the problem you can come up with your own padding scheme so you do not have to transfer to many padding bytes, this could leverage the kind of data you transfer. – zaph May 26 '16 at 20:42

1 Answers1

1

Look at online AES Encryption as reference.

Hex values:
plaintext: 74657374616263 
key:       41413744443430333035394e36373038 
iv:        000102030405060708090A0B0C0D0E0F 

I'm not tested follows code, please test it on your pc.

DC (Dynamic C)

#use AES_CRYPT.LIB
#define AES_CFB_BLOCK_SIZE 16
#define PLAINTEXT_SIZE 7

//41413744443430333035394e36373038
const char key[AES_CFB_BLOCK_SIZE] = {
   '\x41', '\x41', '\x37', '\x44', '\x44', '\x34', '\x30', '\x33',
   '\x30', '\x35', '\x39', '\x4e', '\x36', '\x37', '\x30', '\x38'
};

//000102030405060708090A0B0C0D0E0F
const char iv[AES_CFB_BLOCK_SIZE] = {
   '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
   '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F'
};

//testabc = 74657374616263
const char plntxt[PLAINTEXT_SIZE] =
{
   't', 'e', 's', 't', 'a', 'b', 'c'
};

int main(void) {
   auto int i;
   auto char text[256];
   auto AESstreamState encrypt_state, decrypt_state;

   printf("Test case 1 - encrypt aes cfb\n");
   AESinitStream(&encrypt_state, key, iv);

   memcpy(text, plntxt, sizeof(plntxt));
   AESencryptStream(&encrypt_state, text, AES_CFB_BLOCK_SIZE);
   printf("Encrypted text:\n");
   for (i = 0; i < sizeof(plntxt); i++) {
      printf("%02x.", (int) text[i]);
      if (0 == ((i+1) % PLAINTEXT_SIZE)) printf("\n");
   }
   printf("\n");


   printf("Test case 2 - decrypt aes cfb \n");
   AESinitStream(&decrypt_state, key, iv);

   //memcpy(text, cyptxt, sizeof(cyptxt));
   AESdecryptStream(&decrypt_state, text, AES_CFB_BLOCK_SIZE);
   printf("Decrypted text:\n");
   for (i = 0; i < sizeof(cyptxt); i++) {
      printf("%02x.", (int) text[i]);
      if (0 == ((i+1) % PLAINTEXT_SIZE)) printf("\n");
   }
   printf("\n");

   return 0;
}

C#

using System;
using System.IO;
using System.Security.Cryptography;


namespace TestAES_CFB
{
    class Program
    {

        static byte[] AES_CFB_Encrypt(string plainText, byte[] Key, byte[] IV)
        {
            byte[] encrypted;
            using (Aes aes = Aes.Create())
            {
                aes.KeySize = 128; // 16 bytes
                aes.BlockSize = 128; // 16 bytes
                aes.Key = Key;
                aes.IV = IV;
                aes.Padding = PaddingMode.Zeros;
                aes.Mode = CipherMode.CFB;

                ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }

            return encrypted;
        }

        static string AES_CFB_Decrypt(byte[] cipherText, byte[] Key, byte[] IV)
        {
            string plaintext = null;

            using (Aes aes = Aes.Create())
            {
                aes.KeySize = 128; // 16 bytes
                aes.BlockSize = 128; // 16 bytes
                aes.Key = Key;
                aes.IV = IV;
                aes.Padding = PaddingMode.Zeros;
                aes.Mode = CipherMode.CFB;


                ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }

            }
            return plaintext;
        }


        static void Main(string[] args)
        {
            const int AES_CFB_BLOCK_SIZE = 16;
            const int PLAINTEXT_SIZE = 7;

            //41413744443430333035394e36373038
            byte[] key = new byte[AES_CFB_BLOCK_SIZE] {0x41, 0x41, 0x37, 0x44, 0x44, 0x34, 0x30, 0x33, 0x30, 0x35, 0x39, 0x4e, 0x36, 0x37, 0x30, 0x38};

            //000102030405060708090A0B0C0D0E0F
            byte[] iv = new byte[AES_CFB_BLOCK_SIZE] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};

            //testabc = 74657374616263
            string plntxt = "testabc";

            using (Aes myAes = Aes.Create())
            {

                byte[] encrypted = AES_CFB_Encrypt(plntxt, key, iv);

                string decrypted  = AES_CFB_Decrypt(encrypted, key, iv);

                Console.WriteLine("Encrypted:  {0}", BitConverter.ToString(encrypted));
                Console.WriteLine("Decrypted: {0}", decrypted);
                Console.ReadLine();
            }

        }
    }
}
Ihdina
  • 950
  • 6
  • 21