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é