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

typedef unsigned long int word32;
typedef unsigned long u4;
typedef unsigned char u1;

/* An RC5 context needs to know how many rounds it has, and its subkeys. */

typedef struct {
    u4 *xk;
    int nr;
} rc5_ctx;

/* Where possible, these should be replaced with actual rotate instructions.
   For Turbo C++. ths is done with _lrotl and _lrotr. */


#define ROTL32(X,C)  ( ( (X) << (C) ) | ( (X) >> (32 - (C) ) ) )
#define ROTR32(X,C)  ( ( (X) >> (C) ) | ( (X) << (32 - (C) ) ) )


/* Fuction prototypes for dealing with RC5 basic operations. */
void rc5_init(rc5_ctx *, int);
void rc5_destroy(rc5_ctx *);
void rc5_key(rc5_ctx *, u1 *, int);
void rc5_encrypt(rc5_ctx *, u4 *, int);
void rc5_decrypt(rc5_ctx *, u4 *, int);

/* Function implementations for RC5. */

/* Scrub out all sensitive values. */
void rc_destroy(rc5_ctx *c) {
    int i;

    for(i = 0; i < (c->nr) * 2 + 2; i++) c->xk[i] = 0;
    free(c->xk);
}

/* Allocate memory for rc5 context's xk and such. */
void rc5_init(rc5_ctx *c, int rounds) {
    c->nr = rounds;
    c->xk = (u4 *)malloc(4 * (rounds * 2 + 2));
}

void rc5_encrypt(rc5_ctx *c, u4 *data, int blocks) {
    u4 *d, *sk;
    int h, i, rc;

    d = data;
    sk = (c->xk) + 2;
    for (h = 0; h < blocks; h++){
        d[0] += c->xk[0];
        d[1] += c->xk[1];
        for (i = 0; i < c->nr * 2; i+=2 ) {
            d[0] ^= d[1];
            rc = d[1] & 31;
            d[0] = ROTL32(d[0], rc);
            d[0] += sk[i];
            d[1] ^= d[0];
            rc = d[0] & 31;
            d[1] = ROTL32(d[1],rc);
            d[1] += sk[i+1];
/*            printf("Round %03d : %08lx %08lx sk= %08lx %08lx\n",
                    i / 2, d[0], d[1], sk[i], sk[i+1]);*/
        }
        d += 2;
    }
}

void rc5_decrypt(rc5_ctx *c, u4 *data, int blocks) {
    u4 *d, *sk;
    int h, i, rc;

    d = data;
    sk = (c->xk)+2;
    for(h = 0; h < blocks; h++) {
        for(i = c->nr*2 - 2; i >= 0; i -= 2) {
/*            printf("Round %03d : %08lx %08lx  sk: %08lx %08lx\n",
                    i/2, d[0], d[1], sk[i], sk[i+1]);*/
            d[1] -= sk[i+1];
            rc = d[0] & 31;
            d[1] = ROTR32(d[1], rc);
            d[1] ^= d[0];

            d[0] -= sk[i];
            rc = d[1] & 31;
            d[0] = ROTR32(d[0], rc);
            d[0] ^= d[1];
        }
        d[0] -= c->xk[0];
        d[1] -= c->xk[1];

        d += 2;
    }
}


void rc5_key(rc5_ctx *c, u1 *key, int keylen) {
    u4 *pk, A, B;   /* padded key */
    int xk_len, pk_len, i, num_steps, rc;
    u1 *cp;

    xk_len = c->nr * 2 + 2;
    pk_len = keylen / 4;
    if ((keylen % 4) != 0) pk_len += 1;

    pk = (u4 *) malloc(pk_len * 4);
    if (pk == NULL) {
        printf("An error occured!\n");
        exit(-1);
    }

    /* Initialize pk -- this should work on Intel machines, anyway.... */
    for(i = 0; i < pk_len; i++) pk[i] = 0;
    cp = (u1 *)pk;
    for(i = 0; i < keylen; i++) cp[i] = key[i];

    /* Initialize xk. */
    c->xk[0] = 0xb7e15163;  /* P32 */
    for(i = 0; i < xk_len; i++) c->xk[i] = c->xk[i-1] + 0x9e3779b9; /* Q32 */

    /* TESTING */
    A = B = 0;
    for(i = 0; i < xk_len; i++) {
        A = A + c->xk[i];
        B = B ^ c->xk[i];
    }

    /* Expand key into xk. */
    if (pk_len > xk_len) num_steps = 3*pk_len;
    else num_steps = 3*xk_len;

    A = B = 0;
    for(i = 0; i < num_steps; i++) {
        A = c->xk[i % xk_len] = ROTL32(c->xk[i % xk_len] + A + B, 3);
        rc = (A + B) & 31;
        B = pk[i % pk_len] = ROTL32(pk[i % pk_len] + A + B, rc);
    }

    /* Clobber sensitive data before deallocating memory. */
    for(i = 0; i < pk_len; i++) pk[i] = 0;

    free(pk);
}




void main(void) {
    rc5_ctx c;
    u4 data[8];
    char key[] = "Twas brillig, and the slithy toves";
    int i;

    printf("---------RC5 cipher----------\n");

    for(i = 0; i < 8; i++) data[i] = i;

    rc5_init(&c, 20);   /* 20 rounds */

    rc5_key(&c, key, strlen(key));

    rc5_encrypt(&c, data, 4);
    printf("Encryptions:\n");
    for(i = 0; i < 8; i+= 2) printf("Block %01d = %08lx %08lx\n", i/2, data[i], data[i+1]);

    rc5_decrypt(&c, data, 4);
    printf("Decryptions:\n");
    for(i = 0; i < 8; i+= 2) printf("Block %01d = %08lx %08lx\n", i/2, data[i], data[i+1]);
}

