Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simply encrypt a string in C

I'm trying to encrypt a query string on a game I'm making when opening a url. It doesn't have to be complicated, in fact since I'm working from a game engine it needs to be as simple as possible. It tends to fuss if I get too low level.

I've already created the query string, I just need to take each char of it and subtract 15 from the char to lightly encrypt it. I'm just wanting to make a simple encryption that will deter most users.

I wish I could give a code example but I'm not too experienced in C, and I'm not even sure where to begin. The game engine's api usually makes everything simple for me.

like image 424
Isaiah Avatar asked Oct 01 '11 20:10

Isaiah


1 Answers

None of these answers really constitute any form of reasonable encryption.

What you actually want to do, is use some form of authenticated encryption, and some form of secure key derivation algorithm. My personal recommendation is libsodium. It provides very good defaults, and an API that is relatively hard to get wrong.

There's several different ways to do this:

  1. Secret key encryption with a random key Authenticated Encryption
  2. Secret key encryption with a key derived from a passphrase Key Derivation
  3. Hybrid encryption with Key Agreement. Public Key Encryption

All of these possibilities are integrated into libsodium and implementable with relative ease.

The following code examples are taken directly from the libsodium documentation.

For 1:

#define MESSAGE ((const unsigned char *) "test")
#define MESSAGE_LEN 4
#define CIPHERTEXT_LEN (crypto_secretbox_MACBYTES + MESSAGE_LEN)

unsigned char nonce[crypto_secretbox_NONCEBYTES];
unsigned char key[crypto_secretbox_KEYBYTES];
unsigned char ciphertext[CIPHERTEXT_LEN];

/* Generate a secure random key and nonce */
randombytes_buf(nonce, sizeof nonce);
randombytes_buf(key, sizeof key);
/* Encrypt the message with the given nonce and key, putting the result in ciphertext */
crypto_secretbox_easy(ciphertext, MESSAGE, MESSAGE_LEN, nonce, key);

unsigned char decrypted[MESSAGE_LEN];
if (crypto_secretbox_open_easy(decrypted, ciphertext, CIPHERTEXT_LEN, nonce, key) != 0) {
    /* If we get here, the Message was a forgery. This means someone (or the network) somehow tried to tamper with the message*/
}

For 2: (Deriving a key from a password)

#define PASSWORD "Correct Horse Battery Staple"
#define KEY_LEN crypto_secretbox_KEYBYTES

unsigned char salt[crypto_pwhash_SALTBYTES];
unsigned char key[KEY_LEN];

/* Choose a random salt */
randombytes_buf(salt, sizeof salt);

if (crypto_pwhash
    (key, sizeof key, PASSWORD, strlen(PASSWORD), salt,
     crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE,
     crypto_pwhash_ALG_DEFAULT) != 0) {
    /* out of memory */
}

Now, the key-array contains a key that is suitable for the use in the code sample above. Instead of randombytes_buf(key, sizeof key) for generating a random key, we generated a key derived from a user-defined password, and use that for encryption.

3 is the "most complicated" of the 3 types. It is what you use if you have two parties communicating. Each of the parties generates a "keypair", which contains a public and a secret key. With those keypairs, they can together agree on a "shared key" that they can use for encrypting (and signing) data for each other:

#define MESSAGE (const unsigned char *) "test"
#define MESSAGE_LEN 4
#define CIPHERTEXT_LEN (crypto_box_MACBYTES + MESSAGE_LEN)

unsigned char alice_publickey[crypto_box_PUBLICKEYBYTES];
unsigned char alice_secretkey[crypto_box_SECRETKEYBYTES];
crypto_box_keypair(alice_publickey, alice_secretkey);

unsigned char bob_publickey[crypto_box_PUBLICKEYBYTES];
unsigned char bob_secretkey[crypto_box_SECRETKEYBYTES];
crypto_box_keypair(bob_publickey, bob_secretkey);

unsigned char nonce[crypto_box_NONCEBYTES];
unsigned char ciphertext[CIPHERTEXT_LEN];
randombytes_buf(nonce, sizeof nonce);
if (crypto_box_easy(ciphertext, MESSAGE, MESSAGE_LEN, nonce,
                    bob_publickey, alice_secretkey) != 0) {
    /* error */
}

unsigned char decrypted[MESSAGE_LEN];
if (crypto_box_open_easy(decrypted, ciphertext, CIPHERTEXT_LEN, nonce,
                         alice_publickey, bob_secretkey) != 0) {
    /* message for Bob pretending to be from Alice has been forged! */
}

This code first generates both keypairs (typically, this would happen on bob's and alice's machine separately, and they would send each other their public key, while keeping their secret key, well, secret).

Then, a random nonce is generated, and the call to crypto_box_easy(...) encrypts a message from alice to bob (using bob's public key to encrypt, and alice's secret key to make a signature).

Then (after potentially sending the message over the network), the call to crypto_box_open_easy(...) is used by bob to decrypt the message (using his own secret key to decrypt, and alice's public key to verify the signature). If the verification of the message failed for some reason (someone tried to tamper with it), this is indicated by the non-zero return code.

like image 106
sonOfRa Avatar answered Sep 26 '22 01:09

sonOfRa