Back to the Résumé

/* p2p_crypt.c: Jay Miller, 12-2001

 * A small crypto library using a simple Feistal-style network.  The

 * functions p2p_{en|de}crypt() are used by the calling program to

 * encrypt arbitrary buffers of data using the p2p_crypt() function.

 * Callers must also call p2p_setkey() during initialization.

 */
#include "p2p.h"

/*** key maintenence "class" *********************************************/

/* The key to be used.  If key == 0, no encryption is performed. */
static crypt_t __key = 0;

/* Quick setter function.  Must be called during caller's init process. */
void p2p_setkey(crypt_t k) { __key = k; }

/* Quick getter function. */
BOOL p2p_iskeyset(void) { return __key ? TRUE : FALSE; }

/*** encryption routines *************************************************/

/* A function implementing a simple 64-bit block Fiestal-style network.

 * Over NUM_ROUNDS rounds, L and R are XOR'd and swapped with addition

 * as the round function (F()) of L and K.  The function swaps on exit to

 * guarantee invertability.

 *   block_{left|right} : two halves of the 64-bit block to handle

 */
void p2p_crypt(crypt_t *block_left, crypt_t *block_right)
{
   crypt_t left, right, temp;
   int round;

   /* make sure we have a valid key and a real pointer */
   if (!__key || !block_left || !block_right)
      return;

   left = *block_left;
   right = *block_right;
   
   /* a simple Feistel-style routine */
   for (round = 0; round < NUM_ROUNDS; round++) {
      temp = left;
      left = (left + __key) ^ right;
      right = temp;
   }

   /* swap on output to allow decrypts to work symmetrically */
   *block_left = right;
   *block_right = left;
}

/* Wrapper for p2p_crypt to handle encryption of arbitrary data

 *    plaintext : arbitrary unencrypted data

 *    len       : size of plaintext in bytes

 *

 * Calls p2p_crypt for len bytes of plaintext.  Also handles padding.

 *

 * Returns a pointer to the encrypted text on success or NULL on error.

 * Note that this pointer must be freed with p2p_padding_free().

 */
char *p2p_encrypt(char *plaintext, size_t *len)
{
   crypt_t *block;
   char *paddedtext;
   int current;

   /* little input check */
   if (!(*len) || !plaintext)
      return NULL;

   /* get a new buffer with padding appended for our block cipher */
   if ((paddedtext = p2p_padding_add(plaintext, len)) == NULL)
      return NULL;
   
   block = (crypt_t *)paddedtext;

   /* go through buffer encrypting 1 block at a time */
   for (current = 0; current < (*len) / sizeof(crypt_t); current += 2)
      p2p_crypt(&(block[current]), &(block[current + 1]));

   return paddedtext;
}

/* Wrapper for p2p_crypt to handle decryption of arbitrary data

 *    ciphertext : data encrypted via p2p_encrypt

 *    len        : size of ciphertext in bytes

 *

 * Calls p2p_crypt for len bytes of ciphertext.

 *

 * Returns the length of the resulting ciphertext or 0 on error.

 */
size_t p2p_decrypt(char *ciphertext, size_t len)
{
   crypt_t *block;
   int current;

   /* little input check */
   if (!len || !ciphertext)
      return 0;
   
   block = (crypt_t *)ciphertext;

   /* go through buffer decrypting 1 block at a time */
   for (current = 0; current < len / sizeof(crypt_t); current += 2)
      p2p_crypt(&(block[current]), &(block[current + 1]));

   /* remove the appended padding */
   return p2p_padding_remove((char *)ciphertext, len);
}

/*** padding functions ***************************************************/

/* A padding scheme.  This function finds a new length for the input

 * buffer such that at least one byte is available to specify the length

 * of the padding.  If sizeof(buf) % length_old == 0, another full block

 * is added.  The final byte of the new string is set to the number of

 * plaintext bytes in the final block.

 *    buf        : the data to be padded

 *    length_old : the length of argument 1 in bytes

 *

 * Returns a new string with appended padding or NULL on error.

 */
char *p2p_padding_add(char *buf, size_t *length_old)
{
   char *buf_padded;
   size_t length_new;
   int trailers;

   /* set the number of trailing plaintext bytes in the final block */
   trailers = *length_old % BLOCK_SIZE;

   if (trailers) /* just pad the final block .. */
      length_new = (*length_old / BLOCK_SIZE + 1) * BLOCK_SIZE;
   else          /* .. or append a new block? */
      length_new = *length_old + BLOCK_SIZE;
   
   /* We first allocate a buffer the size of the input buffer rounded

    * up to the nearest block of BLOCK_SIZE bytes.  Then set the new

    * buffer to all zeros and copy the user's data into it.  Finally,

    * append the number of plaintext bytes in the final block. */
   if ((buf_padded = (char *)malloc(length_new)) != NULL) {
      memset(buf_padded, 0, length_new);
      memcpy(buf_padded, buf, *length_old); 
      buf_padded[length_new - 1] = trailers;
   } else {
      p2p_error(WARN, __LINE__, "Could not malloc %d bytes for padding.\n",
            length_new);
   }
            
   /* return the new length of the buffer to the caller */
   *length_old = length_new;

   return buf_padded;
}

/* Remove appended padding.  The reverse of p2p_padding_add(), this

 * function examines the last byte of the plaintext buf and subtracts

 * (BLOCK_SIZE - that number) from the end of the buffer.

 *    buf    : the padded plaintext

 *    length : the length of argument 1

 *

 * Returns the length of the full plaintext or 0 on error.

 */
size_t p2p_padding_remove(char *buf, size_t length)
{
   int trailers;

   /* find the number of trailing plaintext bytes in the padding */
   trailers = buf[length - 1];
   if (trailers > BLOCK_SIZE)  /* simple error checking */
      return 0;

   return length - (BLOCK_SIZE - trailers);
}

/* Frees a buffer allocated by p2p_padding_add(). */
void p2p_padding_free(char *buf)
{
   if (buf)
      free(buf);
}



Back to the Résumé