0

I've quite a few problems with a very small C program I've been writing. Looked through all the stack overflow articles I could find but haven't had much success. The program is suppose to use a very simple XOR "encryption" on a plain text string. Both input string and key are 6 chars long. I'm new to C and pointers. I think i'm failing to grasp some the fundamentals of the language.

#include <stdio.h>
#include <string.h>

#define LENGTH 7
#define KEY "secret"

char * encryptDecrypt(char *plaintext);

int main(int argc, char **argv)
{
    if(argc > 1)
    {
        char *plainText = argv[1];
        printf("Encrypting plaintext: %s (%i)\n", plainText, strlen(plainText));
        char *cipherText = encryptDecrypt(plainText);
        printf("Encrypted: %s (%i)\n", cipherText, strlen(cipherText));
        char *decryptedText = encryptDecrypt(cipherText);
        printf("Decrypted: %s (%i)\n", decryptedText, strlen(decryptedText));
    }
    return 0;
}

char * encryptDecrypt(char *text)
{
    char result[LENGTH];
    for (int i = 0; i < LENGTH-1; i++)
    {
        result[i] = (char)(text[i] ^ KEY[i]);
    }
    char *resultPtr = &result;
    return resultPtr;
}

Running the program with arg "foobar" outputs:

Encrypting plaintext: foobar (6)

Encrypted: ╠╠╠╠╠╠╠╠T¨ (19)

Decrypted: ╠╠╠╠╠╠╠╠T¨ (19)

Problems:

  1. Printing the pointer to the result array is different when used in the encryptDecrypt function and after it has been returned
  2. Using XOR on the cipher text isn't reverting it back to the original plain text (although because whatever i'm printing is wrong, this part may be alright)
  3. The string length of the encrypted/decrypted text is 19 chars long? How is that possible if the original string was 6 chars?
grhm
  • 68
  • 2
  • 9
  • 1
    `char result[LENGTH]; ... char *resultPtr = &result; return resultPtr;`. No. You can not return the address of a local variable. That's undefined behavior. – Andrew Henle Feb 12 '16 at 18:00
  • 1
    Possible duplicate of [Pointer to local variable](http://stackoverflow.com/questions/4570366/pointer-to-local-variable) – 2501 Feb 12 '16 at 18:01
  • Security notes: 1) good to scrub plain text buffers after using them `memset(decryptedText, 0, its_size)` 2) Using `strdup()` to duplicate plaint text data is a problem that works against #1. Best to use your own code only when manipulating plain text data rather than potential leaving copies of it laying around in memory. – chux - Reinstate Monica Feb 12 '16 at 19:00

4 Answers4

4
 char *resultPtr = &result;
 return resultPtr;

You can't do this. result doesn't exist when function ends, you can't return result.

Modify your function like this:

void encryptDecrypt(char *text, char *result)
{
  for (int i = 0; i < LENGTH - 1; i++)
  { 
    result[i] = (char)(text[i] ^ KEY[i]);
  }
}

create an array at the caller site, and pass it as result. e.g.

char result[LENGTH] = {0}; 
encryptDecrypt(plainText, result);

NOTE: Actually using %s for printing encrypted data is not best idea, because for example as a result of XOR you may get a null byte in between the text, which will be considered as a null terminator for your string, and printf won't show rest of the string. Consider something like this for printing cipher text.

Your format specifier for strlen is also wrong, use %zu insted of %i, or you will trigger undefined behaviour.

Community
  • 1
  • 1
Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
4

you cant return a pointer to a local variable. You have to either

  • pass in the address of a buffer
  • malloc space and return it (the caller then has to release it)
  • strdup the local variable and return that (this is really just malloc wrapped up for you)

The reason you can't return a pointer to local variables is because they are destroyed when you exit the function

pm100
  • 48,078
  • 23
  • 82
  • 145
  • `strdup()` will not work on `result`. `strdup()`relies on the null characters indicates the end. `result[i] = (char)(text[i] ^ KEY[i]);` breaks that contract. Need to malloc/copy `LENGTH` or `LENGTH+1`. – chux - Reinstate Monica Feb 12 '16 at 18:42
3

Your encryptDecrypt is returning the address of a local variable. That memory is invalid once the function returns, so attempting to use it is undefined behavior.

You should use malloc to dynamically allocate the memory for the result of this function. Also, you should pass in the size of the string so you know how much space to allocate.

Printing the encrypted string as a string is also undefined behavior, because what you have is not a string but an array of characters which is not null terminated.

Also, be sure to use the %zu format specifier when printing the result of sizeof.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LENGTH 7
#define KEY "secret"

char * encryptDecrypt(char *text, int len);

int main(int argc, char **argv)
{
    if(argc > 1)
    {
        char *plainText = argv[1];
        // use the string length + 1 to include the null terminator
        int len = strlen(plainText)+1;
        printf("Encrypting plaintext: %s (%zu)\n", plainText, strlen(plainText));
        char *cipherText = encryptDecrypt(plainText,len);
        //printf("Encrypted: %s (%zu)\n", cipherText, strlen(cipherText));
        char *decryptedText = encryptDecrypt(cipherText,len);
        printf("Decrypted: %s (%zu)\n", decryptedText, strlen(decryptedText));
        // clean up the allocated memory
        free(cipherText);
        free(decryptedText);
    }
    return 0;
}

char * encryptDecrypt(char *text, int len)
{
    char *result = malloc(len);   // allocate memory for the result
    for (int i = 0; i < len; i++)
    {
        // if the text is longer that the key, wrap around on the key
        result[i] = (char)(text[i] ^ KEY[i%LENGTH]);
    }
    return result;    // return the allocated buffer
}

EDIT:

Fixed the indexing on KEY to prevent overruning it.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • didn't downvote but I think it is null terminated, check, he is encrypting only first 6 letters – Giorgi Moniava Feb 12 '16 at 18:12
  • @GiorgiMoniava I passed in `strlen(plainText)+1` for the encrypt/decrypt length so that the null terminator would get picked up. That way, it doesn't depend on the length of the string and it removes the need to manually null terminate after decryption. – dbush Feb 12 '16 at 18:14
  • I didn't criticize your code - I said his is null terminated even when encrypted I guess – Giorgi Moniava Feb 12 '16 at 18:15
  • @GiorgiMoniava In the original code the encrypted text is not null terminated. It copies the first 6 encrypted bytes into result and leaves the 7th uninitialized. – dbush Feb 12 '16 at 18:17
  • isn't 7th initialized, otherwise how does plainText get printed? in first printf? isn't argv[1] pointing to a null terminated string? – Giorgi Moniava Feb 12 '16 at 18:17
  • 1
    @GiorgiMoniava The plaintext is of course null terminated since it's an element of `argv`. It's the encrypted text that isn't null terminated. – dbush Feb 12 '16 at 18:19
  • yep true, maybe you wanna highlight that in your answer more concretely. Anyway the way he was returning is UB so I missed that somewhere .. – Giorgi Moniava Feb 12 '16 at 18:22
2

The result character array in encryptDecrypt function is a local variable to this function only and it's content will remain in the memory as long as encryptDecrypt function is executing. Once the function execution finishes array result content may or may not be present in the memory.(If the content is destroyed then it will contain some garbage value).

Array name itself points to the first element of the array.
So char *resultPtr = &result; is sending the address of pointer pointing to first element. Instead you should write char *resultPtr = &result[0]; or char *resultPtr = result;

To make your code working, replace
char result[LENGTH];
with

 char * result = (char*)malloc(sizeof(LENGTH));
 result[LENGTH-1] = '\0';      // To make result string null terminated

malloc function is used for dynamic memory allocation, now the content present in result array will stay in the memory(even after the execution of the function is over) until it is explicitly deallocated from the memory using free function.

NOTE: Even though result string after encryption is null terminated but we can not use printf("%s",result); to print it because encryption method uses XOR which even can make other characters of string as null terrminator. To print such a string you have to create your own function to print first 6 bytes of the result array .

  • `(char *)` cast is not necessary, as `malloc` returns `void*`. but good answer. – LittleByBlue Feb 12 '16 at 18:34
  • only replacing with malloc won't help, see answer by dbush. 7th byte is not null terminated in OPs case. – Giorgi Moniava Feb 12 '16 at 18:35
  • "array result contest is destroyed (it will then contain some garbage value)" ---> confident C does not specify this. Instead usage of `&result` after the function ends is UB. Data might be there, might not. – chux - Reinstate Monica Feb 12 '16 at 18:38
  • @Giorgi Moniava After encryption, the `char` array should not be handled as a string as there may exist embedded null characters. So appending a null character has limited value. – chux - Reinstate Monica Feb 12 '16 at 18:46
  • @Giorgi Moniava To make the string null terminated, I have explicitly modified the 7th byte. Alternatively, we can use calloc instead of malloc to adjust this need automatically. – Harsh Gupta Feb 12 '16 at 18:59
  • @chux Modified the code. You are right. (Data might be there, might not). – Harsh Gupta Feb 12 '16 at 19:01
  • @HarshGupta Null terminator will not help, see chux's comment above, I had mentioned that before in my answer though too. – Giorgi Moniava Feb 12 '16 at 19:01
  • A pedantic note since post addresses encryption: C does not specify memory is "destroyed". Neither does C specified "destroyed" data contains "garbage". Data that goes out of scope has undefined values and undefined access to them. Good encryption code deterministically handles buffers, insuring former buffers are wiped. Consider the bad guys can use memory dumps. But good answer for a new guy. – chux - Reinstate Monica Feb 12 '16 at 19:12