A series of curiously simple Feistel-type ciphers were developed in the 1990's by Ronald Rivest (of RSA fame). RC5 is basically a Feistel structure, where each round function consists of:

- XOR the two sides together.
- Bit rotate one side by the value of the other side.
- Add a key-dependent value.

A = A + S[0]; B = B + S[1]; for (i = 1 ; i <= R ; i++) { A = A ^ B; A = rotateLeft(A, B) + S[2*i];

B = B ^ A; B = rotateLeft(B, A) + S[(2*i)+1]; }

That's it!

A decent description interspersed with the C implementation is in RFC 2040. Steve Burnett wrote up a readable design intro and some technical discussion here.

RC5 with a short 56-bit key was brute forced in 250 days (in 1997), mostly as a PR stunt to show DES's key was too short.

RC5 with a 64-bit key was brute forced in 1,757 days using way more computers (in 2002).

They've been trying for a 72-bit key since 2002.

Typical minimum today is a 128 bit key and 20 rounds, which seems to be secure.

One reason RC5 isn't more widely used? It's patented, although the patent does run out in November 2015.

Here's a simplified version, without a key schedule, for
differential cryptanalysis testing.

/* A cipher similar to RC5: */ typedef unsigned long var; /* Bit rotate rightwards */ var ror(var v,unsigned int bits) { return (v>>bits)|(v<<(8*sizeof(var)-bits)); } /* Bit rotate left */ var rol(var v,unsigned int bits) { return (v<<bits)|(v>>(8*sizeof(var)-bits)); } /* This class is used to encrypt or decrypt a block of data. */ class cipherblock { public: var L,R; // current state, Left and Right halves // Perform one forward round, using this key. // Modifies L depending on both R and the round key. void round_fw(var &L,const var &R,var key,unsigned int stage) { L=L^R; L=rol(L,R); L=L+key+stage; } // Perform one reverse round, using this key. // Modifies L depending on both R and the round key. void round_bw(var &L,const var &R,var key,unsigned int stage) { L=L-key-stage; L=ror(L,R); L=L^R; } // Print the current state of this block void print(const char *description,int stage) { printf("%s %d: %016lx %016lx\n",description,stage,L,R); } }; int foo(void) { for (int diff=0;diff<100;diff++) { var plain=0xabcdef0000000+diff; // plaintext var key=0xf0f0f0f; // encryption key int r; // round count cipherblock b; b.L=plain; b.R=plain>>32; if (diff==0) b.print(" P",0); // Take several rounds to scramble the data // FIXME: real RC5 uses more rounds, and a real key schedule. for (r=0;r<10;r++) { b.round_fw(b.L,b.R,key,r); if (diff==0) b.print("L",r); b.round_fw(b.R,b.L,key,r); if (diff==0) b.print("R",r); } b.print(" C",0); // Walk rounds backwards to unscramble the data for (r--;r>=0;r--) { if (diff==0) b.print("R",r); b.round_bw(b.R,b.L,key,r); if (diff==0) b.print("L",r); b.round_bw(b.L,b.R,key,r); } if (diff==0) b.print(" P",0); } return 0; }