2

I'm trying to be able to decode encoded strings that were encoded using a perl script. The reason I can't just change the perl script is because a lot of important data already exists that was saved via the perl script and it would be too much to decode in perl and recode another way. It's easier just to keep things in place (for now). However, the compiled perl code is 2MB. I want to write it in C so the executable will be a smaller size.

What I have so far is below. It won't work though. It basically gives me junk output. The problem I think is that the perl script used a hex-based encryption. How do I go about decoding that? Can someone point me to where I went wrong?

/*
Test to decode perl-encrypted string.
NOTE: Not all code written by me. Function code is either written by or derived
      from code from other people in response to similar questions found on the
      internet.

Required Lib: crypt (-lcrypt)

Perl Code from existing script (that is being converted to C):

    use Crypt::CBC;
    use Crypt::DES;
    my $text = "thisisalongtest";
    my $salt_key = "fOagmJOKu2SF";
    my $cipher = Crypt::CBC->new( -key => $salt_key, -cipher => 'DES' -header => 'none');
    my $enc_text = $cipher->encrypt_hex($text);
Perl crypt functions require libcrypt-cbc-perl & libcrypt-des-perl

Data:

Original Text: thisisalongtest
Salt Key: fOagmJOKu2SF
Resulting Encrypted String: 53616c7465645f5f167ebac84042fe7ceac836e1d3e7d3aa1dfc27e0e8cad0f1

Resulting output:

Decrypted: (unprintable junk characters)
*/

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

#define BUFFSIZE 420

int encrypt(char key[9], char salt[9], char pass[BUFFSIZE], int mode);
void enc_from_hex(const char* st, char ret_result[BUFFSIZE]);
int hex_to_int(char c);
int hex_to_ascii(char c, char d);

int main (int argc, char *argv[]) {

    int err;
    char passwd[BUFFSIZE];
    char result[BUFFSIZE];
    char key[13];

    sprintf(key,"fOagmJOKu2SF");

    /* Change this from hex pairs to ASCII */
    sprintf(passwd, "53616c7465645f5f167ebac84042fe7ceac836e1d3e7d3aa1dfc27e0e8cad0f1");
    enc_from_hex(passwd, result);

    /* Decrypt */
    err = encrypt(key, "", result, 1); /* DO NOT use 'NULL' for 2nd parameter! */
    if(err != 0) {
        printf("Error.\n");
    } else {
        printf("Decrypted: %s\n", result);
    }

    return 0;
}

/* Encryption */
int encrypt(char key[13], char salt[13], char pass[BUFFSIZE], int mode){
    char temp[13];
    strcpy(temp, salt);
    int buffsize;
    int errcode;

    des_setparity(key);
    buffsize = strlen(pass);

    /* Pad pass to ensure size is divisable by 8.*/
    while (buffsize % 8 && buffsize<BUFFSIZE) {
        pass[buffsize++] = '\0';
    }

    /* Determine Function */
    if(mode == 1) {
        errcode = cbc_crypt(key, pass, buffsize, DES_DECRYPT | DES_SW, temp);
    } else {
        errcode = cbc_crypt(key, pass, buffsize, DES_ENCRYPT | DES_SW, temp);
    }

    if (DES_FAILED(errcode) || strcmp(pass, "") == 0) {
        return errcode;
    }

    return errcode;
}

/* Hex conversion functions */
void enc_from_hex(const char* st, char ret_result[BUFFSIZE]) {
    char temp[2];

    int length = strlen(st);
    int i;
    char buf = 0;
    for(i = 0; i < length; i++) {
        if(i % 2 != 0) {
            sprintf(temp, "%c", hex_to_ascii(buf, st[i]));
            strcat(ret_result, temp);
        } else {
            buf = st[i];
        }
    }

}

int hex_to_int(char c) {
    int first = c / 16 - 3;
    int second = c % 16;
    int result = first*10 + second;
    if(result > 9) result--;
    return result;
}

int hex_to_ascii(char c, char d) {
    int high = hex_to_int(c) * 16;
    int low = hex_to_int(d);
    return high+low;
}
  • For what it's worth, I can decode your resulting encrypted string with Crypt::CBC and Crypt::DES and your `$salt_key` in Perl. – simbabque Jul 28 '15 at 14:42
  • When you don't use `-literal_key => 1`, the value passed to `-key` isn't actually used as the key. It's hashed first. (This is a very good practice.) You can see the transformation in `_key_from_key` in Crypt::CBC. I suspect this is a difference. It may not be the only one. – ikegami Jul 28 '15 at 14:55
  • @simbabque - I need to do be able to decrypt this in C. I can already encrypt it in Perl, yes. But I have to be able to decrypt it in C because as my comments say, I need to convert the code to C and still be able to use pre-existing data. –  Jul 28 '15 at 17:47

1 Answers1

0

As @ikegami points out in the comments the value passed to key Crypt::CBC is not really a key. This value is passed to another function which combines it with a random salt then hashes it to generate the real key and initialization vector. The random salt is saved with the cypher text the idea being if you encrypt the same data with the same key more than once the output will be different every time.

If you convert your encrypted string to ascii you will notice the first eight characters spell Salted__, this corresponds to the format used by OpenSSL.

See below for a somewhat crude example using OpenSSL's EVP api:

//
// compile with: gcc -o crypt crypt.c -lssl -lcrypto
//
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>

int main(int argc, char *argv[]){
  char pass[]="fOagmJOKu2SF";
  char text[]="53616c7465645f5f167ebac84042fe7ceac836e1d3e7d3aa1dfc27e0e8cad0f1";

  int i = 0;
  char *pos=text;
  while(*pos){ // converts hex string to binary in place
    sscanf(pos, "%2hhx", &text[i++]);
    pos += 2;
  } text[i]=0;

  EVP_CIPHER_CTX ctx;
  unsigned char key[8] = {0};
  unsigned char  iv[8] = {0};

  int len;
  char *clear = malloc(strlen(text)); 

  //                                             v-> First Charicter after 'Salted__' 
  EVP_BytesToKey(EVP_des_cbc(), EVP_md5(), &text[8], pass, strlen(pass), 1, key, iv);

  EVP_DecryptInit(&ctx, EVP_des_cbc(), key, iv);
  //                                          v-> Cypertext starts after salt
  EVP_DecryptUpdate(&ctx, clear, &len, &text[16], strlen(text)-15);
  EVP_DecryptFinal(&ctx, clear, &len);

  printf("%s\n", clear);
  return 0;
}

http://www.ict.griffith.edu.au/anthony/info/crypto/openssl.hints https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html https://www.openssl.org/docs/manmaster/crypto/EVP_EncryptInit.html

Finbar Crago
  • 432
  • 5
  • 12