1

TL;DR...

I need to encrypt an in-RAM C struct, byte-for-byte, using OpenSSL / EVP RC4 stream-cipher. How do I implement EVP (e.g. EVP_CipherUpdate) to accomplish the actual encryption of bytes in the struct?

The Details...

I have a ISAM/BTree database that needs its records/data encrypted. Don't worry about that, just know that each "record" is a C struct with many members (fields). This has been working for like 15 years (don't ask, the codebase is from the K&R C days, I think). The ISAM overhead simply takes a bytestream (the struct w/data) as an argument when writing the record... specifically, the ISAM's "write" function accepts a pointer to the data/structure.

Anyway, I'm trying to implement an OpenSSL stream cipher (RC4) -via the EVP framework- that can just sit between my program and the ISAM, so as to simply hand the ISAM my encrypted bytes, without him knowing or caring. I might add that I think the ISAM doesn't care about the structure of the data or even that it's a struct... it just gets raw data, I believe.

My struct is like this simplified example (in reality there are many more varied members):

typedef struct
  UCHAR flag;
  char field2[30];
  int qty;
  } DB_REC;

How would I (if it's even possible) go about encrypting the entire structure (in-place, even), byte for byte, if necessary? I've tried testing with simple strings, even; but can't get that to work, either.

I have another file called crypto.c (and .h) where I'm building my functions to encrypt and decrypt whatever I "pass" to them (might be a string, a struct, whatever - that's why my arg is void). For example:

void encrypt_db_rawData(void *data_to_encrypt, size_t data_size)
  {
  unsigned char buf_out[data_size];
  int buf_out_byteCount = 0;
  buf_out_byteCount = sizeof(buf_out);

  EVP_CIPHER_CTX ctx; //declare an EVP context
  int keyBytes = 16;  //size of RC4 key in bytes

  /* ... my_keystr is acquired from a file elsewhere ... */
  /* ... my_iv is a global defined elsewhere ... */

  EVP_CIPHER_CTX_init(&ctx);
  EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1);
  EVP_CIPHER_CTX_set_key_length(&ctx, keyBytes);
  EVP_CipherInit_ex(&ctx, NULL, NULL, my_keystr, my_iv, 1);

  int byte_i;
  for( byte_i = 0; byte_i < sizeof(data_to_encrypt); i++ )
    {
    EVP_CipherUpdate(&ctx, buf_out, &buf_out_byteCount, data_to_encrypt[byte_i], 1);
    }

  *data_to_encrypt = buf_out;  //overwrite the original data with its encrypted version
  }

C is not my first language ~ especially C89 (or older perhaps), and I'm in way over my head on this one - this got thrown into my lap due to some restructuring; so I appreciate any constructive help anyone can offer. I'm in pointer-HELL!

csr19us
  • 105
  • 9
  • 1
    `sizeof(data_to_encrypt)` will always return `sizeof(T*)` (probably `8` on 64 bit, `4` on 32 bit). You need to pass its length as an additional argument. – cadaniluk Oct 06 '15 at 17:03
  • Do you have any error? Otherwise you could just pass one of these `DB_REC`s to `encrypt_db_rawData` (and its length, see my comment above). – cadaniluk Oct 06 '15 at 17:05
  • @cad My debugging indicates a value of 8 for this, so I believe you're right in that it's giving me the size of the pointer. How would I get the size of the data/struct, instead? Must I get it beforehand and pass it as a 2nd argument to the function? – csr19us Oct 06 '15 at 17:06
  • Yep, just that. A `size_t` is probably fitted well in that case. – cadaniluk Oct 06 '15 at 17:07
  • 1
    Your are also indexing a `void*` pointer with `data_to_encrypt[byte_i]` but the compiler doesn't know the data type, that is, the size of each element. – Weather Vane Oct 06 '15 at 17:08
  • I've added the 2nd arg for size and it compiles fine. But my malloc isn't taking the size of my struct (which is 3712). If I print sizeof(buf_out), I get 8. If I print sizeof(*buf_out), I get 1. – csr19us Oct 06 '15 at 17:12
  • 1
    `*data_to_encrypt = buf_out;` is not going to work either. It will attempt to overwrite the data itself passed, with the `malloc()`ed pointer. – Weather Vane Oct 06 '15 at 17:14
  • 1
    `buf_out` behaves similarly. `sizeof(unsigned char*) == 8` and `sizeof(*buf_out) == sizeof(unsigned char) == 1` – cadaniluk Oct 06 '15 at 17:14
  • @WeatherVane, thanks. I'll take those into consideration once I get my argument sizes right, first. – csr19us Oct 06 '15 at 17:20
  • @cad, I've changed my malloc to a simple char-array declaration. Will update my code above, shortly. – csr19us Oct 06 '15 at 17:24
  • OP has confused the issue by altering the question in line with suggestions. Please don't keep updating your code posted. If you need to, ask a new question. – Weather Vane Oct 06 '15 at 17:25
  • @WeatherVane, sorry... my mind's just all over the place today. My intention was really just to avoid confusion. At any rate, I think I've gotten to the point of diving into your suggestions. Coding now, and will report back. Thanks, again. – csr19us Oct 06 '15 at 17:29
  • You will probably need `memcpy()`. – Weather Vane Oct 06 '15 at 17:29
  • @WeatherVane, you're right about the "indexing a void*" comment. My compiler is complaining: "warning: dereferencing `void *' pointer" ~ any ideas for this newb about how to construct that particular line? – csr19us Oct 06 '15 at 17:35
  • If it takes all kind of input, you need to make a cast to the type that is expected by the xxCipherxx functions, possibly `unsigned char`, I don't know. Whatever type the cipher needs, declare a pointer, say `unsigned char *cptr = data_to_encrypt;` and work with `cptr`. – Weather Vane Oct 06 '15 at 17:38
  • @WeatherVane, if it simplifies matters, my data will always be the DB_REC struct that's already defined. Would it be better to prototype my function that way? ~~ BTW, the cipher routine is prototyped as: `int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, unsigned char *in, int inl);` – csr19us Oct 06 '15 at 17:48
  • So why did you need `void*` argument in the first place? Perhaps you have now answered your own question! – Weather Vane Oct 06 '15 at 17:57
  • @WeatherVane, the previous developer coded about a dozen different structs (one for each database). So, there's not just "DB_REC". I want an encryption function that will work for any struct passed to it. I used void in an attempt at abstraction... I'd like to avoid having to update several dozen routines... or to avoid having to create a dozen encryption routines with the first argument being unique for each defined struct. – csr19us Oct 06 '15 at 18:02

0 Answers0