4

So I have this code, which basically, encrypts two plain text messages and then try to decrypt them, and print. The problem is that first message is recovered fine but the second is garbage. I downloaded this code from this tutorial and then modified it to work with strings instead of files as I need it for sending encrypted text over sockets. So length of the plaintext won't be known to other endpoint, is there a way to find the length or will I have to send the length of plaintext along with the cipher somehow?

Now, I think there is a problem in the break condition of decrypt.

Also, is the main() code right conceptually: encrypt messages with updating state and then reset state and decrypt messages with updating state?

And is there a way to find out the actual length of cipher text (not the buffer)?

This is just a dummy program that I was trying out to understand how AES CTR will work.

#include <openssl/aes.h>
#include <openssl/rand.h> 
#include <openssl/hmac.h>
#include <openssl/buffer.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

// Code example uses partail code from: http://stackoverflow.com/questions/3141860/aes-ctr-256-encryption-mode-of-operation-on-openssl
// Mostly in the ctr_ state, and init_ctr functions. 

struct ctr_state 
{ 
    unsigned char ivec[AES_BLOCK_SIZE];  
    unsigned int num; 
    unsigned char ecount[AES_BLOCK_SIZE]; 
}; 

int init_ctr(struct ctr_state *state, const unsigned char iv[16])
{        
    /* aes_ctr128_encrypt requires 'num' and 'ecount' set to zero on the
     * first call. */
    state->num = 0;
    memset(state->ecount, 0, AES_BLOCK_SIZE);

    /* Initialise counter in 'ivec' to 0 */
    memset(state->ivec + 8, 0, 8);

    /* Copy IV into 'ivec' */
    memcpy(state->ivec, iv, 8);
}

void fencrypt(char* text, char* cipher, const unsigned char* enc_key, struct ctr_state* state)
{ 
    AES_KEY key;
    unsigned char indata[AES_BLOCK_SIZE]; 
    unsigned char outdata[AES_BLOCK_SIZE];
    int offset=0;
    //Initializing the encryption KEY
    if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
    {
        fprintf(stderr, "Could not set encryption key.");
        exit(1); 
    }

    //Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext  
    while(1)    
    {
        printf("while going\n");
        memcpy(indata, text+offset, AES_BLOCK_SIZE); 
        AES_ctr128_encrypt(indata, outdata, AES_BLOCK_SIZE, &key, state->ivec, state->ecount, &state->num);

        memcpy(cipher+offset, outdata, AES_BLOCK_SIZE);
        offset=offset+AES_BLOCK_SIZE;
        if (offset > strlen(text))
        {
            break;
        }
    }
}

void fdecrypt(char* cipher, char* text, const unsigned char* enc_key, struct ctr_state* state)
{   
    AES_KEY key;
    unsigned char indata[AES_BLOCK_SIZE]; 
    unsigned char outdata[AES_BLOCK_SIZE];
    int offset=0;
    //Initializing the encryption KEY
    if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
    {
        fprintf(stderr, "Could not set decryption key.");
        exit(1);
    }

    //Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext       
    while(1)    
    {
        memcpy(indata, cipher+offset, AES_BLOCK_SIZE);  
        //printf("%i\n", state.num);
        AES_ctr128_encrypt(indata, outdata, AES_BLOCK_SIZE, &key, state->ivec, state->ecount, &state->num);

        memcpy(text+offset, outdata, AES_BLOCK_SIZE); 
        offset=offset+AES_BLOCK_SIZE;
        if (offset > strlen(cipher))
        {
            break;
        }
    }
}

int main(int argc, char *argv[])
{
    unsigned char iv[AES_BLOCK_SIZE];
    struct ctr_state state;
    char* plain="quick brown fox jumped over the lazy dog what ";
    char* plain2="a dog he is idiot who is the genius ";
    char cipher[128];
    char cipher2[128];
    char recovered[128];
    char recovered2[128];
    const unsigned char* enc_key="123456789abcdef0";

    if(!RAND_bytes(iv, AES_BLOCK_SIZE))
    {
        fprintf(stderr, "Could not create random bytes.");
        exit(1);    
    }
    init_ctr(&state, iv); //Counter call
    printf("Plain text length:%lu\n",strlen(plain));
    // BIO_dump_fp(stdout, plain, strlen(plain));
    // printf("Plain text:%s\n",plain);
    fencrypt(plain, cipher,enc_key,&state);
    fencrypt(plain2, cipher2,enc_key,&state);
    // cipher[strlen(plain)]='\0';
    // BIO_dump_fp(stdout, cipher, strlen(plain));
    init_ctr(&state, iv); //Counter call
    fdecrypt(cipher,recovered,enc_key,&state);
    fdecrypt(cipher2,recovered2,enc_key,&state);
    // printf("Cipher text length:%lu\n",strlen(cipher));
    printf("Recovered text:%s\n",recovered);
    printf("Recovered text:%s\n",recovered2);
    return 0;
}
Wajahat
  • 1,593
  • 3
  • 20
  • 47

1 Answers1

4

CTR mode doesn't need separate encrypt and decrypt method. Encryption key can be set once. OpenSSL's AES_ctr128_encrypt takes care of most of the work, so the code can be simplified.

Also we really need vector test. Here we are just testing with random text "quick brown fox...", we get back the same text, but there is NO guarantee that 'cipher' was correct, and wether or not the encryption is AES quality. I'll add a quick vector test later if there is time.

void init_ctr(struct ctr_state *state, const unsigned char iv[16])
{        
    state->num = 0;
    memset(state->ecount, 0, 16);
    memcpy(state->ivec, iv, 16);
}

void crypt_message(const u8* src, u8* dst, unsigned int src_len, const AES_KEY* key, const u8* iv)
{   
   struct ctr_state state;
   init_ctr(&state, iv);
   AES_ctr128_encrypt(src, dst, src_len, key, state.ivec, state.ecount, &state.num);
}

int main()
{
   int len;
   char source[128];
   char cipher[128];
   char recovered[128];
   unsigned char iv[AES_BLOCK_SIZE];

   const unsigned char* enc_key = (const unsigned char*)"123456789abcdef0";

   if(!RAND_bytes(iv, AES_BLOCK_SIZE)) 
   {
       fprintf(stderr, "Could not create random bytes.");
       exit(1);    
   }

   AES_KEY key;
   AES_set_encrypt_key(enc_key, 128, &key);

   strcpy(source, "quick brown fox jumped over the lazy dog what.");
   len = strlen(source);
   memset(recovered, 0, sizeof(recovered));
   crypt_message((const u8*)source, (u8*)cipher, len, &key, iv);
   crypt_message((const u8*)cipher, (u8*)recovered, len, &key, iv);
   printf("Recovered text:%s\n", recovered);

   strcpy(source, "a dog he is idiot who is the genius.");
   len = strlen(source);
   memset(recovered, 0, sizeof(recovered));
   crypt_message((const u8*)source, (u8*)cipher, len, &key, iv);
   crypt_message((const u8*)cipher, (u8*)recovered, len, &key, iv);
   printf("Recovered text:%s\n", recovered);

   return 0;
}

To encrypt/decrypt files, or send/receive:

void crypt_file(const u8* src_file, const u8* dst_file, const AES_KEY* key, const u8* iv)
{   
   struct ctr_state state;
   init_ctr(&state, iv);

   const int buffer_size = 512; //not less than 16
   unsigned char buffer_in[buffer_size];
   unsigned char buffer_out[buffer_size];
   int bytes_read;

   //open files and/or socket
   //file/message loop
   {
      //read source, obtain buffer_in and bytes_read
      AES_ctr128_encrypt(buffer_in, buffer_out, bytes_read, key, state.ivec, state.ecount, &state.num);
      //write buffer_out/bytes_read to destination
   }
   //close handles
}

In your code, fdecrypt() contains strlen(cipher). However cipher is pure binary data, strlen doesn't work with it. You have to supply the length manually. I added len parameter to fdecrypt. In main I use strlen(plaintext) for simplicity, though it should be the true length of cipher data. Changes are denoted by ##change

void fdecrypt(unsigned int len, char* cipher, char* text, const unsigned char* enc_key, struct ctr_state* state)
{   
    AES_KEY key;
    unsigned char indata[AES_BLOCK_SIZE]; 
    unsigned char outdata[AES_BLOCK_SIZE];
    int offset=0;
    //Initializing the encryption KEY
    if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
    {
        fprintf(stderr, "Could not set decryption key.");
        exit(1);
    }

    //Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext       
    while(1)    
    {
        memcpy(indata, cipher+offset, AES_BLOCK_SIZE);  
        //printf("%i\n", state.num);
        AES_ctr128_encrypt(indata, outdata, AES_BLOCK_SIZE, &key, state->ivec, state->ecount, &state->num);

        memcpy(text+offset, outdata, AES_BLOCK_SIZE); 
        offset=offset+AES_BLOCK_SIZE;
        //if (offset > strlen(cipher))##changed
        if (offset > len)
        {
            break;
        }
    }
}

int main(int argc, char *argv[])
{
    unsigned char iv[AES_BLOCK_SIZE];
    struct ctr_state state;
    char* plain="quick brown fox jumped over the lazy dog what ";
    char* plain2="a dog he is idiot who is the genius ";
    char cipher[128];
    char cipher2[128];
    char recovered[128];
    char recovered2[128];
    const unsigned char* enc_key=(const unsigned char*)"123456789abcdef0";

    if(!RAND_bytes(iv, AES_BLOCK_SIZE))
    {
        fprintf(stderr, "Could not create random bytes.");
        exit(1);    
    }

    init_ctr(&state, iv); //Counter call
    printf("Plain text length:%lu\n",strlen(plain));
    // BIO_dump_fp(stdout, plain, strlen(plain));
    // printf("Plain text:%s\n",plain);
    fencrypt(plain, cipher,enc_key,&state);
    fencrypt(plain2, cipher2,enc_key,&state);
    // cipher[strlen(plain)]='\0';
    // BIO_dump_fp(stdout, cipher, strlen(plain));
    init_ctr(&state, iv); //Counter call
    fdecrypt(strlen(plain), cipher,recovered,enc_key,&state);//##changed
    fdecrypt(strlen(plain2), cipher2,recovered2,enc_key,&state);//##changed
    // printf("Cipher text length:%lu\n",strlen(cipher));
    printf("Recovered text:%s\n",recovered);
    printf("Recovered text:%s\n",recovered2);
    return 0;
}
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • I understand what you are saying and I knew that previously, but the problem is (as I mentioned in the question), I will be sending the message encrypted on the network, so other side will not know the length of plain text unless I send the length separately. So I am looking for a way to find the length just from the cipher, how are the encryption schemes over networks actually implemented ? – Wajahat Apr 04 '15 at 23:48
  • 1
    Barmak is correct here; there's no way to determine the length from random data (and the output of AES looks like random data). Don't think of this as "sending encrypted data on the network." Think of it as "sending data on the network." Almost all binary protocols include a length field. – Rob Napier Apr 05 '15 at 00:06
  • Okay got your point, so if I was using TCP stream sockets in C, can I add the payload length in packet (I know there is a length field in IP header) which can be recovered at the other end? How will I go about doing that, sorry I am new to c socket programming. – Wajahat Apr 06 '15 at 05:44
  • Wajahat, you are making this complicated. You should be asking separate question about TCP/IP. Here you say you knew strlen was causing error, but you put it there anyway. I just updated the answer with standard code, though it's a different method now. To send/receive data over network, you have to assign a buffer length. Maybe this is data streaming where send/receive size is not known in advance, you can always send a separate message to indicate message size or end of message. – Barmak Shemirani Apr 06 '15 at 14:12