3

I want to do an encryption / decryption program in C based on something I did in perl. The compiled perl program is 2MB so I figure if I write it in C it will be a smaller executable size.

My problem is, while I get it to encrypt, I can't get it to decrypt. It's been literally ages since I last used C so I forgot a lot of stuff. Someone please enlighten me to what I'm doing wrong here? Thanks.

/*
    ============================================================================
    Name        : test-c.c
    Description : Testing Project
                  Trying to do a C version of this perl code:
                    my $cipher = Crypt::CBC->new( -key => $salt_key, -cipher => 'DES' -header => 'none');
                    my $enc_text = $cipher->encrypt_hex($text);
    Requires    : -lcrypt
    References  :
      Function: cbc_crypt (char *key, char *blocks, unsigned len, unsigned mode, char *ivec)
      GNU C Library: DES Encryption (http://www.gnu.org/software/libc/manual/html_node/DES-Encryption.html#DES-Encryption)
      cbc_crypt (http://unix.derkeiler.com/Newsgroups/comp.unix.programmer/2012-10/msg00023.html)
    ============================================================================
    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <rpc/des_crypt.h>

    int main(void) {
        char key[]     = "aBcDeFg1";
        char pass[]    = "mypass1234test";
        char encbuff[] = "87654321";
        char decbuff[] = "87645321";
        int buffsize;
        int result;

        des_setparity(key);

        /* Encrypt pass, result is in encbuff */
        buffsize = sizeof(pass);
        /* Add to pass to ensure size is divisable by 8. */
        while (buffsize % 8) {
            pass[buffsize++] = '\0';
        }

        printf("Encrypted: ");
        result = cbc_crypt(key, pass, buffsize, DES_ENCRYPT | DES_SW, encbuff);
        if (DES_FAILED(result) || strcmp(encbuff, "") == 0) {
            if(strcmp(encbuff, "") == 0) {
                printf("*** Null Output ***\n");
            } else {
                printf("*** Encryption Error ***\n");
            }
        } else {
            printf("%s\n", encbuff);
        }

        /* Decrypt encbuff, result is in decbuff */

        /* FIXME: Decryption doesn't work:
            Encrypted: ,�7&���8
            Decrypted: *** Decryption Error ***
        */
        buffsize = sizeof(encbuff);
        /* Add to pass to ensure size is divisable by 8. */
        while (buffsize % 8) {
            encbuff[buffsize++] = '\0';
        }

        printf("Decrypted: ");
        result = cbc_crypt(key, encbuff, buffsize, DES_DECRYPT | DES_SW, decbuff);
        if(DES_FAILED(result) || strcmp(decbuff, "") == 0) {
            if(strcmp(encbuff, "") == 0) {
                printf("*** Null Output ***\n");
            } else {
                printf("*** Decryption Error ***\n");
            }
        } else {
            printf("%s\n", decbuff);
        }

        return 0;
    }
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • `sizeof(encbuff)` is a compile-time constant, you can't then go and write characters beyond that array size! I'm not sure what the comment means by that code either... – Brian Sidebotham Jul 02 '15 at 15:22
  • Thank you, Brian. I see your point about overrunning the array. I didn't really think at first that it might be the problem. As I mention, it's been ages since I've used C. :) I'll have to be very sure in the future to watch those array sizes. –  Jul 02 '15 at 19:30

1 Answers1

2

As noticed by @Brian_Sidebotham , using char str[10];buffsize=sizeof(str);...str[buffsize++]='\0' is not a good thing in c. Writing beyond the end of an array may trigger undefined behavior. It could work or deliver wrong results or stop due to a segmentation fault. So the first thing to do is to work with larger buffers. For instance :

char key[9];
sprintf(key,"12345678");

char password[420];
sprintf(password,"i am not going to write my password here.");

Notice that it is 9, not 8, to leave some place for the null terminating character \0 that is automatically added by sprintf() to build a valid c string.

Moreover, the last parameter of cbc_crypt() is not the output. It is not the encrypted string but an 8-byte initialization vector for the chaining. That way, if you need to encrypt multiple strings, it prevents regularities in the clear text from appearing in the cipher text. As the cipher text is to be decrypted, the same initialization vector must be provided. Encryption is performed in place : the input string pass will be overwritten by the encrypted text.

Finally, make sure that your strings are large enough because the encrypted text seems to be slightly larger that the clear text.

The following code is expected to work. It is compiled by gcc main.c -o main

/*
    ============================================================================
    Name        : test-c.c
    Description : Testing Project
                  Trying to do a C version of this perl code:
                    my $cipher = Crypt::CBC->new( -key => $salt_key, -cipher => 'DES' -header => 'none');
                    my $enc_text = $cipher->encrypt_hex($text);
    Requires    : -lcrypt
    References  :
      Function: cbc_crypt (char *key, char *blocks, unsigned len, unsigned mode, char *ivec)
      GNU C Library: DES Encryption (http://www.gnu.org/software/libc/manual/html_node/DES-Encryption.html#DES-Encryption)
      cbc_crypt (http://unix.derkeiler.com/Newsgroups/comp.unix.programmer/2012-10/msg00023.html)
    ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rpc/des_crypt.h>

#define BUFFSIZE 420
int main(void) {
    //  char key[]     = "aBcDeFg1";
    char key[9];
    sprintf(key,"aBcDeFg1");
    //char pass[]    = "mypass1234test";
    char pass[BUFFSIZE];
    sprintf(pass,"mypass1234test");

    //  char encbuff[] = "87654321";
    char ivec[9];
    sprintf(ivec,"87654321");
    //  char decbuff[] = "87645321";
    char ivectemp[9];
    strcpy(ivectemp,ivec);
    int buffsize;
    int result;

    des_setparity(key);

    /* Encrypt pass, result is in encbuff */
    buffsize = strlen(pass);
    printf("buffsize is %d\n",buffsize);
    /* Add to pass to ensure size is divisable by 8. */
    while (buffsize % 8 && buffsize<BUFFSIZE) {
        pass[buffsize++] = '\0';
    }
    printf("pass is %s\n",pass);
    printf("buffsize is %d\n",buffsize);
    printf("Encrypted: ");
    result = cbc_crypt(key, pass, buffsize, DES_ENCRYPT | DES_SW, ivectemp);
    if (DES_FAILED(result) || strcmp(pass, "") == 0) {
        if(strcmp(pass, "") == 0) {
            printf("*** Null Output ***\n");
        } else {
            printf("*** Encryption Error ***\n");
        }
    } else {
        printf("%s\n", pass);
    }

    /* Decrypt encbuff, result is in decbuff */

    /* FIXME: Decryption doesn't work:
            Encrypted: ,�7&���8
            Decrypted: *** Decryption Error ***
     */
    buffsize = strlen(pass);
    printf("buffsize is %d\n",buffsize);
    /* Add to pass to ensure size is divisable by 8. */
    while (buffsize % 8 && buffsize<BUFFSIZE) {
        pass[buffsize++] = '\0';
    }
    printf("buffsize is %d\n",buffsize);
    printf("Decrypted: ");
    //keeping the same initialization vector for decrypting, encrypt has altered ivectemp
    strcpy(ivectemp,ivec);
    result = cbc_crypt(key, pass, buffsize, DES_DECRYPT | DES_SW, ivectemp);
    if(DES_FAILED(result) || strcmp(pass, "") == 0) {
        if(strcmp(pass, "") == 0) {
            printf("*** Null Output ***\n");
        } else {
            printf("*** Decryption Error ***\n");
        }
    } else {
        printf("%s\n",pass);
    }

    return 0;
}

Now... A few words about security... According to the wikipedia page about the Data Encryption Standard NBS DES :

DES is now considered to be insecure for many applications. This is mainly due to the 56-bit key size being too small; in January, 1999, distributed.net and the Electronic Frontier Foundation collaborated to publicly break a DES key in 22 hours and 15 minutes

You may switch to the implementation of AES by the openssl library for instance. See How to do encryption using AES in Openssl or Simple AES encryption decryption with openssl library in C or AES (aes-cbc-128, aes-cbc-192, aes-cbc-256) encryption/decryption with openssl C for instance.

Community
  • 1
  • 1
francis
  • 9,525
  • 2
  • 25
  • 41
  • Thank you for all the useful information. Given that DES is now considered insecure (something I had not known) you really didn't *have* to fix the code but I greatly appreciate the fact that you did provide working code for me to study (I tested it and it does work). And learning the reasons why my code did not work was very helpful. Thank you. I have a pre-existing perl script I want to convert to C which is why I'm doing this but at some point I will want to take your advice and change the encryption type. –  Jul 02 '15 at 19:29