RFC 2040
The RC5, RC5-CBC, RC5-CBC-Pad, and RC5-CTS Algorithms
Okay bestie, this is the RFC that gave us RC5 and its whole crew of ciphers ๐๐ The title block is giving early internet royalty: RFC 2040, "The RC5, RC5-CBC, RC5-CBC-Pad, and RC5-CTS Algorithms," from the Network Working Group, dated October 1996. The authors are R. Baldwin (RSA Data Security, Inc.) and R. Rivest (MIT Laboratory for Computer Science and RSA Data Security, Inc.). It's marked Category: Informational, which means it's sharing info, not setting an Internet standard. The title block doesn't list any "obsoletes" or "updates" lines, so there's nothing it replaces or amends ๐ธ
Status of this Memo
This memo gives information to the Internet community ๐ซถ It does not specify an Internet standard of any kind, and it can be shared freely with no limits on distribution ๐ฆ
Acknowledgments
Huge thanks to Steve Dusse, Victor Chang, Tim Mathews, Brett Howard, and Burt Kaliski for their helpful suggestions ๐ โจ
Table of Contents
1. Executive Summary ....................... 1
2. Overview ................................ 2
3. Terminology and Notation ................ 3
4. Description of RC5 Keys ................. 4
5. Description of RC5 Key Expansion ........ 6
6. Description of RC5 Block Cipher ......... 10
7. Description of RC5-CBC and RC5-CBC-Pad .. 12
8. Description of RC5-CTS .................. 18
9. Test Program and Vectors ................ 19
10. Security Considerations ................. 26
11. ASN.1 Identifiers ....................... 28
References ........................................ 28
Authors' Addresses ................................ 29
1. Executive Summary โจ
okay so this document defines four ciphers in enough detail that different implementations can actually talk to each other ๐ The first is the raw RC5 block cipher. It takes a fixed size input block and spits out a fixed size output block, using a transformation that depends on a key ๐
The second is RC5-CBC, which is the Cipher Block Chaining (CBC) mode for RC5. It can handle messages whose length is a multiple of the RC5 block size ๐งฉ The third is RC5-CBC-Pad, which handles plaintext of any length. The ciphertext can be longer than the plaintext, but by at most the size of a single RC5 block ๐ The fourth is RC5-CTS, the Cipher Text Stealing mode of RC5. It also handles plaintext of any length, and the ciphertext length matches the plaintext length exactly ๐
RC5 was invented by Professor Ronald L. Rivest of the Massachusetts Institute of Technology in 1994. It's very fast and very simple, and it's parameterized by the block size, the number of rounds, and the key length. Those parameters can be tuned to balance security, performance, and exportability, which is such a flex ๐
RSA Data Security Incorporated has filed a patent application on the RC5 cipher, and it has also filed for trademark protection for RC5, RC5-CBC, RC5-CBC-Pad, RC5-CTS, and assorted variations ๐
2. Overview
this memo is a restatement of material that's already been published ๐ The description of RC5 follows the notation and order of explanation in the original RC5 paper by Professor Rivest [2]. The CBC mode shows up in reference works like the one by Bruce Schneier [6]. The CBC-Pad mode is the same as the one in the Public Key Cryptography Standard (PKCS) number five [5]. Sample C code [8] is included just for clarity, and it's equivalent to the English descriptions ๐ซถ
The ciphers get explained in a bottom up, object-oriented way ๐ง First come the RC5 keys and the key expansion algorithm. Second is the RC5 block cipher. Finally, the RC5-CBC and RC5-CBC-Pad ciphers are specified. To keep things brief, only the encryption process is described. Decryption is just encryption run backwards, by inverting the steps ๐
The object-oriented description should make it easier to build interoperable systems, even though it isn't as terse as the functional descriptions in the references ๐ There are two classes of objects, keys and cipher algorithms. Both classes share operations that create and destroy these objects in a way that makes sure secret information never gets handed back to the memory manager ๐
Keys also have a "set" operation that copies a secret key into the object. The "set" operation for cipher objects defines the number of rounds and the initialization vector ๐
There are four operations for the cipher objects in this memo. You can bind a key to a cipher object. You can set a new initialization vector without changing the key. You can encrypt part of a message (which happens multiple times for long messages). And you can process the last part of a message, which may add padding or check the message length ๐
So in summary, the cipher is explained in terms of these operations:
RC5_Key_Create - Create a key object.
RC5_Key_Destroy - Destroy a key object.
RC5_Key_Set - Bind a user key to a key object.
RC5_CBC_Create - Create a cipher object.
RC5_CBC_Destroy - Destroy a cipher object.
RC5_CBC_Encrypt_Init - Bind a key object to a cipher object.
RC5_CBC_SetIV - Set a new IV without changing the key.
RC5_CBC_Encrypt_Update - Process part of a message.
RC5_CBC_Encrypt_Final - Process the end of a message.
3. Terminology and Notation
okay the term "word" means a string of bits of a set length that you can operate on as either an unsigned integer or a bit vector ๐งฎ A word might be 32 or 64 bits long, depending on the block size you want for RC5. A 32 bit word gives you a 64 bit block size. For best performance, the RC5 word size should match the CPU's register size ๐ The term "byte" means eight bits ๐
Here are the variables used all through this memo and what they mean:
Wis the word size for RC5, measured in bits. It's half the block size. The word sizes covered by this memo are 32 and 64 โจWWis the word size for RC5, measured in bytes ๐Bis the block size for RC5, measured in bits. It's twice the word size. When RC5 is used as a 64 bit block cipher,Bis 64 andWis 32. The range is0 < B < 257. In the sample code,B,is used as a variable instead of a cipher system parameter, but that's obvious from context ๐BBis the block size for RC5, measured in bytes.BB = B / 8๐งbis the byte length of the secret key, with0 <= b < 256๐Kis the secret key, treated as a sequence ofbbytes indexed byK[0], ..., K[b-1]๐๏ธRis the number of rounds of the inner RC5 transform, with0 <= R < 256๐Tis the number of words in the expanded key table. It's always2*(R + 1), with1 < T < 513๐Sis the expanded key table, treated as a sequence of words indexed byS[0], ..., S[T-1]๐งNis the byte length of the plaintext message ๐Pis the plaintext message, treated as a sequence ofNbytes indexed byP[0], ..., P[N-1]๐Cis the ciphertext output, treated as a sequence of bytes indexed byC[0], C[1], ...๐Iis the initialization vector for the CBC mode, treated as a sequence of bytes indexed byI[0], ..., I[BB-1]๐ซถ
4. Description of RC5 Keys
so like most block ciphers, RC5 takes a small user key and expands it into a table of internal keys ๐๏ธ The byte length of the user key is one of the cipher's parameters, so the RC5 user key object has to be able to hold keys of variable length. A possible C structure for this looks like:
/* Definition of RC5 user key object. */
typedef struct rc5UserKey
{
int keyLength; /* In Bytes. */
unsigned char *keyBytes;
} rc5UserKey;
The basic operations on a key are create, destroy, and set. To avoid exposing key material to other parts of an application, the destroy operation zeros the memory allocated for the key before releasing it to the memory manager ๐ A general key object may support other operations too, like generating a new random key or deriving a key from key-agreement information ๐
4.1 Creating an RC5 Key
To create a key, the memory for the key object has to be allocated and initialized. The C code below assumes a function called "malloc" will return a block of uninitialized memory from the heap, or zero if there's an error ๐
/* Allocate and initialize an RC5 user key.
* Return 0 if problems.
*/
rc5UserKey *RC5_Key_Create ()
{
rc5UserKey *pKey;
pKey = (rc5UserKey *) malloc (sizeof(*pKey));
if (pKey != ((rc5UserKey *) 0))
{
pKey->keyLength = 0;
pKey->keyBytes = (unsigned char *) 0;
}
return (pKey);
}
4.2 Destroying an RC5 Key
To destroy a key, the memory has to be zeroed and released to the memory manager ๐งน The C code below assumes a function called "free" will return a block of memory to the heap ๐ธ
/* Zero and free an RC5 user key.
*/
void RC5_Key_Destroy (pKey)
rc5UserKey *pKey;
{
unsigned char *to;
int count;
if (pKey == ((rc5UserKey *) 0))
return;
if (pKey->keyBytes == ((unsigned char *) 0))
return;
to = pKey->keyBytes;
for (count = 0 ; count < pKey->keyLength ; count++)
*to++ = (unsigned char) 0;
free (pKey->keyBytes);
pKey->keyBytes = (unsigned char *) 0;
pKey->keyLength = 0;
free (pKey);
}
4.3 Setting an RC5 Key
Setting the key object makes a copy of the secret key into a block of memory allocated from the heap ๐
/* Set the value of an RC5 user key.
* Copy the key bytes so the caller can zero and
* free the original.
* Return zero if problems
*/
int RC5_Key_Set (pKey, keyLength, keyBytes)
rc5UserKey *pKey;
int keyLength;
unsigned char *keyBytes;
{
unsigned char *keyBytesCopy;
unsigned char *from, *to;
int count;
keyBytesCopy = (unsigned char *) malloc (keyLength);
if (keyBytesCopy == ((unsigned char *) 0))
return (0);
from = keyBytes;
to = keyBytesCopy;
for (count = 0 ; count < keyLength ; count++)
*to++ = *from++;
pKey->keyLength = count;
pKey->keyBytes = keyBytesCopy;
return (1);
}
5. Description of RC5 Key Expansion
this section describes the key expansion algorithm ๐ To be specific, the sample code assumes the block size is 64 bits, and several programming parameters depend on that block size ๐
/* Definitions for RC5 as a 64 bit block cipher. */
/* The "unsigned int" will be 32 bits on all but */
/* the oldest compilers, which will make it 16 bits. */
/* On a DEC Alpha "unsigned long" is 64 bits, not 32. */
#define RC5_WORD unsigned int
#define W (32)
#define WW (W / 8)
#define ROT_MASK (W - 1)
#define BB ((2 * W) / 8) /* Bytes per block */
/* Define macros used in multiple procedures. */
/* These macros assumes ">>" is an unsigned operation, */
/* and that x and s are of type RC5_WORD. */
#define SHL(x,s) ((RC5_WORD)((x)<<((s)&ROT_MASK)))
#define SHR(x,s,w) ((RC5_WORD)((x)>>((w)-((s)&ROT_MASK))))
#define ROTL(x,s,w) ((RC5_WORD)(SHL((x),(s))|SHR((x),(s),(w))))
5.1 Definition of initialization constants ๐
Okay so, there are two magic constants, Pw and Qw, and they're defined for any word size W like this ๐ฎ
Pw = Odd((e-2)*2**W)
Qw = Odd((phi-1)*2**W)
Where e is the base of the natural logarithm (2.71828 ...), phi is the golden ratio (1.61803 ...), 2**W means 2 raised to the power of W, and Odd(x) gives you x if it's already odd, or x + 1 if it's even. โจ For W equal to 16, 32, and 64, the Pw and Qw constants are these hex values:
#define P16 0xb7e1
#define Q16 0x9e37
#define P32 0xb7e15163
#define Q32 0x9e3779b9
#define P64 0xb7e151628aed2a6b
#define Q64 0x9e3779b97f4a7c15
#if W == 16
#define Pw P16 /* Select 16 bit word size */
#define Qw Q16
#endif
#if W == 32
#define Pw P32 /* Select 32 bit word size */
#define Qw Q32
#endif
#if W == 64
#define Pw P64 /* Select 64 bit word size */
#define Qw Q64
#endif
5.2 Interface definition ๐
So the key expansion routine takes your b-byte secret key, K, and turns it into an expanded key, S. S is a sequence of T = 2*(R+1) words, and it's built using two constants that come from e and phi. First those constants initialize S, then S gets modified using K. Here's what a C procedure header for this routine could look like ๐ง
/* Expand an RC5 user key.
*/
void RC5_Key_Expand (b, K, R, S)
int b; /* Byte length of secret key */
char *K; /* Secret key */
int R; /* Number of rounds */
RC5_WORD *S; /* Expanded key buffer, 2*(R+1) words */
{
5.3 Convert secret key from bytes to words ๐ซถ
Step one is converting that b-byte key into a bunch of words stored in the array L. On a little-endian processor, you do this by zeroing out L and then copying in the b bytes of K. The C code below gets the same result on all processors ๐ธ
int i, j, k, LL, t, T;
RC5_WORD L[256/WW]; /* Based on max key size */
RC5_WORD A, B;
/* LL is number of elements used in L. */
LL = (b + WW - 1) / WW;
for (i = 0 ; i < LL ; i++) {
L[i] = 0;
}
for (i = 0 ; i < b ; i++) {
t = (K[i] & 0xFF) << (8*(i%4)); /* 0, 8, 16, 24*/
L[i/WW] = L[i/WW] + t;
}
5.4 Initialize the expanded key table ๐ฆ
Now we fill up the S table with a fixed pattern that doesn't depend on the key at all. It's an arithmetic progression built from Pw and Qw, taken modulo 2**W, so S[i] equals i*Qw + Pw modulo 2**W. You could precompute this table and copy it whenever you need it, or just compute it on the fly. In C it looks like this ๐
T = 2*(R+1);
S[0] = Pw;
for (i = 1 ; i < T ; i++) {
S[i] = S[i-1] + Qw;
}
5.5 Mix in the secret key ๐ฅ
This is where your secret key, K, actually gets mixed into the expanded key, S. First, we work out how many iterations of the mixing function we need, called k. It's set to three times the bigger of two numbers: LL, the number of initialized elements of L, and T, the number of elements in S. ๐
Each iteration kinda works like one pass of the encryption inner loop, where two variables, A and B, get updated by the first and second halves of the iteration. At the start, A and B are zero, and so are the indexes into S, which is i, and into L, which is j. In the first half, we add up S[i], A and B to get a partial result. The new A is that partial result rotated left by three bits, and then A goes into S[i]. The second half adds L[j], A and B together to get a second partial result. That gets rotated left by A+B bit positions and becomes the new B, which goes into L[j]. At the end of each iteration, i and j go up by one, wrapping around modulo the size of their own arrays. In C it's:
i = j = 0;
A = B = 0;
if (LL > T)
k = 3 * LL; /* Secret key len > expanded key. */
else
k = 3 * T; /* Secret key len < expanded key. */
for ( ; k > 0 ; k--) {
A = ROTL(S[i] + A + B, 3, W);
S[i] = A;
B = ROTL(L[j] + A + B, A + B, W);
L[j] = B;
i = (i + 1) % T;
j = (j + 1) % LL;
}
return;
} /* End of RC5_Key_Expand */
6. Description of RC5 Block Cipher โจ
This section walks through how the RC5 block cipher encrypts one single input block. Decryption is just the same steps in reverse, so we're not going to spell that out. RC5 is set up by a version number, V, a round count, R, and a word size in bits, W. This description matches the original version of RC5 (V = 16 decimal), and it covers any positive value for R plus the values 16, 32, and 64 for W. ๐ฆ
The inputs to this process are the expanded key table, S, the number of rounds, R, the input buffer pointer, in, and the output buffer pointer, out. A possible C procedure header for it would be:
void RC5_Block_Encrypt (S, R, in, out)
RC5_WORD *S;
int R;
char *in;
char *out;
{
6.1 Loading A and B values ๐
First, we turn the input bytes into two unsigned integers called A and B. When RC5 is used as a 64 bit block cipher, A and B are 32 bit values. The first input byte becomes the least significant byte of A, and the fourth input byte becomes the most significant byte of A. The fifth input byte becomes the least significant byte of B, and the last input byte becomes the most significant byte of B. This is super efficient on little-endian processors like the Intel family ๐ In C it looks like this:
int i;
RC5_WORD A, B;
A = in[0] & 0xFF;
A += (in[1] & 0xFF) << 8;
A += (in[2] & 0xFF) << 16;
A += (in[3] & 0xFF) << 24;
B = in[4] & 0xFF;
B += (in[5] & 0xFF) << 8;
B += (in[6] & 0xFF) << 16;
B += (in[7] & 0xFF) << 24;
6.2 Iterating the round function ๐
This is the step where the expanded key gets mixed with the input for the core encryption operation. The first two words of the expanded key get added to A and B respectively, and then the round function gets repeated R times. ๐ฅ
The first half of the round function works out a new A from A, B, and the next unused word in the expanded key table. Basically, A gets XOR'ed with B, and that first partial result is rotated to the left by an amount set by B, which gives the second partial result. The rotation happens on a W bit boundary, so it's a 32 bit rotation for the version of RC5 with a 64 bit block size. The actual rotation amount only depends on the least significant log base-2 of W bits of B. Then the next unused word of the expanded key table gets added to that second partial result, and the answer becomes the new A. ๐
The second half of the round function is identical, except the roles of A and B are switched. So B gets exclusive or'ed with A, that first partial result gets rotated to the left by an amount set by A to form the second partial result, and then the next unused word of the expanded key table gets added to it. That becomes the new B. ๐
Here's one way to write it in C:
A = A + S[0];
B = B + S[1];
for (i = 1 ; i <= R ; i++) {
A = A ^ B;
A = ROTL(A, B, W) + S[2*i];
B = B ^ A;
B = ROTL(B, A, W) + S[(2*i)+1];
}
6.3 Storing the A and B values ๐ง
The last step is turning A and B back into a sequence of bytes. It's basically the reverse of the load step. In C it could look like this:
out[0] = (A >> 0) & 0xFF;
out[1] = (A >> 8) & 0xFF;
out[2] = (A >> 16) & 0xFF;
out[3] = (A >> 24) & 0xFF;
out[4] = (B >> 0) & 0xFF;
out[5] = (B >> 8) & 0xFF;
out[6] = (B >> 16) & 0xFF;
out[7] = (B >> 24) & 0xFF;
return;
} /* End of RC5_Block_Encrypt */
7. Description of RC5-CBC and RC5-CBC-Pad ๐ฆ
This section covers the CBC and CBC-Pad modes of the RC5 cipher. It builds on the RC5 key objects and the RC5 block cipher we just went through ๐
7.1 Creating cipher objects ๐
The cipher object has to keep track of the padding mode, the number of rounds, the expanded key, the initialization vector, the CBC chaining block, and an input buffer. In C, a structure definition for that could look like this:
/* Definition of the RC5 CBC algorithm object.
*/
typedef struct rc5CBCAlg
{
int Pad; /* 1 = RC5-CBC-Pad, 0 = RC5-CBC. */
int R; /* Number of rounds. */
RC5_WORD *S; /* Expanded key. */
unsigned char I[BB]; /* Initialization vector. */
unsigned char chainBlock[BB];
unsigned char inputBlock[BB];
int inputBlockIndex; /* Next inputBlock byte. */
} rc5CBCAlg;
To create a cipher algorithm object, you check the parameters first, then allocate space for the expanded key table. The expanded key gets initialized with the method we covered earlier. Last, the state variables (padding mode, number of rounds, and the input buffer) get set to their starting values. In C it goes like this ๐ธ
/* Allocate and initialize the RC5 CBC algorithm object.
* Return 0 if problems.
*/
rc5CBCAlg *RC5_CBC_Create (Pad, R, Version, bb, I)
int Pad; /* 1 = RC5-CBC-Pad, 0 = RC5-CBC. */
int R; /* Number of rounds. */
int Version; /* RC5 version number. */
int bb; /* Bytes per RC5 block == IV len. */
char *I; /* CBC IV, bb bytes long. */
{
rc5CBCAlg *pAlg;
int index;
if ((Version != RC5_FIRST_VERSION) ||
(bb != BB) || (R < 0) || (255 < R))
return ((rc5CBCAlg *) 0);
pAlg = (rc5CBCAlg *) malloc (sizeof(*pAlg));
if (pAlg == ((rc5CBCAlg *) 0))
return ((rc5CBCAlg *) 0);
pAlg->S = (RC5_WORD *) malloc (BB * (R + 1));
if (pAlg->S == ((RC5_WORD *) 0)) {
free (pAlg);
return ((rc5CBCAlg *) 0);
}
pAlg->Pad = Pad;
pAlg->R = R;
pAlg->inputBlockIndex = 0;
for (index = 0 ; index < BB ; index++)
pAlg->I[index] = I[index];
return (pAlg);
}
7.2 Destroying cipher objects ๐
Destroying a cipher object is basically the reverse of creating it. Be careful to zero out the memory before handing it back to the memory manager. In C it could look like this ๐
/* Zero and free an RC5 algorithm object.
*/
void RC5_CBC_Destroy (pAlg)
rc5CBCAlg *pAlg;
{
RC5_WORD *to;
int count;
if (pAlg == ((rc5CBCAlg *) 0))
return;
if (pAlg->S == ((RC5_WORD *) 0))
return;
to = pAlg->S;
for (count = 0 ; count < (1 + pAlg->R) ; count++)
{
*to++ = 0; /* Two expanded key words per round. */
*to++ = 0;
}
free (pAlg->S);
for (count = 0 ; count < BB ; count++)
{
pAlg->I[count] = (unsigned char) 0;
pAlg->inputBlock[count] = (unsigned char) 0;
pAlg->chainBlock[count] = (unsigned char) 0;
}
pAlg->Pad = 0;
pAlg->R = 0;
pAlg->inputBlockIndex = 0;
free (pAlg);
}
7.3 Setting the IV for cipher objects โจ
okay so for CBC cipher objects, the state of the algorithm depends on the expanded key, the CBC chain block, and any internally buffered input ๐ง . Often the same key gets used with tons of messages, and each one has its own unique initialization vector. Making a brand new cipher object every single time is so much overhead ๐ฉ, so it makes more sense to have an operation that lets the caller swap the IV on an existing cipher object. In C, that could look like this:
/* Setup a new initialization vector for a CBC operation
* and reset the CBC object.
* This can be called after Final without needing to
* call Init or Create again.
* Return zero if problems.
*/
int RC5_CBC_SetIV (pAlg, I)
rc5CBCAlg *pAlg;
char *I; /* CBC Initialization vector, BB bytes. */
{
int index;
pAlg->inputBlockIndex = 0;
for (index = 0 ; index < BB ; index++)
{
pAlg->I[index] = pAlg->chainBlock[index] = I[index];
pAlg->inputBlock[index] = (unsigned char) 0;
}
return (1);
}
7.4 Binding a key to a cipher object ๐
the operation that binds a key to a cipher object is the one that does the key expansion ๐ . Key expansion could technically be an operation on keys, but that would not work correctly for ciphers that modify the expanded key as they operate ๐. After expanding the key, this operation must initialize the CBC chain block from the initialization vector and prepare the input buffer to receive the first character. In C, it could be done like this:
/* Initialize the encryption object with the given key.
* After this routine, the caller frees the key object.
* The IV for this CBC object can be changed by calling
* the SetIV routine. The only way to change the key is
* to destroy the CBC object and create a new one.
* Return zero if problems.
*/
int RC5_CBC_Encrypt_Init (pAlg, pKey)
rc5CBCAlg *pAlg;
rc5UserKey *pKey;
{
if ((pAlg == ((rc5CBCAlg *) 0)) ||
(pKey == ((rc5UserKey *) 0)))
return (0);
RC5_Key_Expand (Key->keyLength, pKey->keyBytes,
pAlg->R, pAlg->S);
return (RC5_CBC_SetIV(pAlg, pAlg->I));
}
7.5 Processing part of a message ๐ฆ
the encryption here uses the Init-Update-Final paradigm ๐. The update operation can run on a sequence of message parts, so you incrementally produce the ciphertext bit by bit (well, byte by byte ๐ญ). Once the last part is processed, you call Final to grab any plaintext bytes or padding that are still buffered inside the cipher object. A procedure header for this operation would look something like:
/* Encrypt a buffer of plaintext.
* The plaintext and ciphertext buffers can be the same.
* The byte len of the ciphertext is put in *pCipherLen.
* Call this multiple times passing successive
* parts of a large message.
* After the last part has been passed to Update,
* call Final.
* Return zero if problems like output buffer too small.
*/
int RC5_CBC_Encrypt_Update (pAlg, N, P,
pCipherLen, maxCipherLen, C)
rc5CBCAlg *pAlg; /* Cipher algorithm object. */
int N; /* Byte length of P. */
char *P; /* Plaintext buffer. */
int *pCipherLen;/* Gets byte len of C. */
int maxCipherLen; /* Size of C. */
char *C; /* Ciphertext buffer. */
{
7.5.1 Output buffer size check. ๐
the first step of plaintext processing is making sure the output buffer is big enough to hold the ciphertext ๐ซถ. The ciphertext gets produced in multiples of the block size, and it depends on how many plaintext characters get passed into this operation plus anything already sitting in the cipher object's internal buffer. In C code, that's:
int plainIndex, cipherIndex, j;
/* Check size of the output buffer. */
if (maxCipherLen < (((pAlg->inputBlockIndex+N)/BB)*BB))
{
*pCipherLen = 0;
return (0);
}
7.5.2 Divide plaintext into blocks ๐ฐ
next up, we keep adding characters to the internal buffer until a full block is built ๐งฑ. When that happens, the buffer pointers get reset and the input buffer gets exclusive-or'ed (XORed) with the CBC chaining block ๐. The byte order of the chaining block matches the input block. For example, the ninth input byte gets XORed with the first ciphertext byte. The result then goes into the RC5 block cipher we talked about earlier ๐ฅ. To cut down on data movement and byte alignment problems, the RC5 output can be written straight into the CBC chaining block. Finally, that output gets copied into the ciphertext buffer the user gave you, and before returning, the actual size of the ciphertext is passed back to the caller. In C, this step looks like:
plainIndex = cipherIndex = 0;
while (plainIndex < N)
{
if (pAlg->inputBlockIndex < BB)
{
pAlg->inputBlock[pAlg->inputBlockIndex]
= P[plainIndex];
pAlg->inputBlockIndex++;
plainIndex++;
}
if (pAlg->inputBlockIndex == BB)
{ /* Have a complete input block, process it. */
pAlg->inputBlockIndex = 0;
for (j = 0 ; j < BB ; j++)
{ /* XOR in the chain block. */
pAlg->inputBlock[j] = pAlg->inputBlock[j]
^ pAlg->chainBlock[j];
}
RC5_Block_Encrypt(pAlg->S, pAlg->R
pAlg->inputBlock,
pAlg->chainBlock);
for (j = 0 ; j < BB ; j++)
{ /* Output the ciphertext. */
C[cipherIndex] = pAlg->chainBlock[j];
cipherIndex++;
}
}
}
*pCipherLen = cipherIndex;
return (1);
} /* End of RC5_CBC_Encrypt_Update */
7.6 Final block processing ๐
this step handles the last block of plaintext ๐. For RC5-CBC, it just does error checking to make sure the plaintext length really is a multiple of the block length. For RC5-CBC-Pad, padding bytes get added to the plaintext โจ. All the pad bytes are the same, and each one is set to a value that tells you how many bytes of padding there are. For example, if there are eight bytes of padding, they'll all be hexadecimal 0x08 ๐ธ. There will always be between one and BB padding bytes, inclusive. In C code, that's:
/* Produce the final block of ciphertext including any
* padding, and then reset the algorithm object.
* Return zero if problems.
*/
int RC5_CBC_Encrypt_Final (pAlg, pCipherLen, maxCipherLen, C)
rc5CBCAlg *pAlg;
int *pCipherLen; /* Gets byte len of C. */
int maxCipherLen; /* Len of C buffer. */
char *C; /* Ciphertext buffer. */
{
int cipherIndex, j;
int padLength;
/* For non-pad mode error if input bytes buffered. */
*pCipherLen = 0;
if ((pAlg->Pad == 0) && (pAlg->inputBlockIndex != 0))
return (0);
if (pAlg->Pad == 0)
return (1);
if (maxCipherLen < BB)
return (0);
padLength = BB - pAlg->inputBlockIndex;
for (j = 0 ; j < padLength ; j++)
{
pAlg->inputBlock[pAlg->inputBlockIndex]
= (unsigned char) padLength;
pAlg->inputBlockIndex++;
}
for (j = 0 ; j < BB ; j++)
{ /* XOR the chain block into the plaintext block. */
pAlg->inputBlock[j] = pAlg->inputBlock[j]
^ pAlg->chainBlock[j];
}
RC5_Block_Encrypt(pAlg->S, pAlg->R,
pAlg->inputBlock, pAlg->chainBlock);
cipherIndex = 0;
for (j = 0 ; j < BB ; j++)
{ /* Output the ciphertext. */
C[cipherIndex] = pAlg->chainBlock[j];
cipherIndex++;
}
*pCipherLen = cipherIndex;
/* Reset the CBC algorithm object. */
return (RC5_CBC_SetIV(pAlg, pAlg->I));
} /* End of RC5_CBC_Encrypt_Final */
8. Description of RC5-CTS ๐ฆ
the Cipher Text Stealing (CTS) mode for block ciphers is described by Schneier on pages 195 and 196 of [6] ๐. This mode handles any length of plaintext and produces ciphertext whose length matches the plaintext length, which is so iconic ๐. CTS acts like CBC mode for everything except the last two blocks of the plaintext. Here's how to handle those last two parts, called Pn-1 and Pn, where Pn-1 is exactly one block (BB) long and the last block, Pn, is Ln bytes. Ln ranges from 1 to BB inclusive, so Pn could actually be a complete block ๐.
- Exclusive-or Pn-1 with the previous ciphertext block, Cn-2, to create Xn-1.
- Encrypt Xn-1 to create En-1.
- Select the first Ln bytes of En-1 to create Cn.
- Pad Pn with zeros at the end to create P of length BB.
- Exclusive-or En-1 with P to create to create Dn.
- Encrypt Dn to create Cn-1
- The last two parts of the ciphertext are Cn-1 and Cn respectively.
to implement CTS encryption, the RC5-CTS object has to hold on to (buffer) at most 2*BB bytes of plaintext and process them specially when the RC5_CTS_Encrypt_Final routine is called ๐ซถ.
here's how to decrypt Cn-1 and Cn ๐ :
- Decrypt Cn-1 to create Dn.
- Pad Cn with zeros at the end to create C of length BB.
- Exclusive-or Dn with C to create Xn.
- Select the first Ln bytes of Xn to create Pn.
- Append the tail (BB minus Ln) bytes of Xn to Cn to create En.
- Decrypt En to create Pn-1.
- The last two parts of the plaintext are Pn-1 and Pn respectively.
9. Test Program and Vectors ๐งช
to help confirm an implementation is correct, this section gives a test program and results from a set of test vectors ๐.
9.1 Test Program โจ
okay so bestie, here's a C test program ๐ it reads test vectors from its input stream and writes results to its output stream. the subsections after this give a set of test vectors for inputs and the matching outputs ๐
#include <stdio.h>
#define BLOCK_LENGTH (8 /* bytes */)
#define MAX_KEY_LENGTH (64 /* bytes */)
#define MAX_PLAIN_LENGTH (128 /* bytes */)
#define MAX_CIPHER_LENGTH(MAX_PLAIN_LENGTH + BLOCK_LENGTH)
#define MAX_ROUNDS (20)
#define MAX_S_LENGTH (2 * (MAX_ROUNDS + 1))
typedef struct test_vector
{
int padding_mode;
int rounds;
char keytext[2*MAX_KEY_LENGTH+1];
int key_length;
char key[MAX_KEY_LENGTH];
char ivtext[2*BLOCK_LENGTH+1];
int iv_length;
char iv[BLOCK_LENGTH];
char plaintext[2*MAX_PLAIN_LENGTH+1];
int plain_length;
char plain[MAX_PLAIN_LENGTH];
char ciphertext[2*MAX_CIPHER_LENGTH+1];
int cipher_length;
char cipher[MAX_CIPHER_LENGTH];
RC5_WORD S[MAX_S_LENGTH];
} test_vector;
void show_banner()
{
(void) printf("RC5 CBC Tester.\n");
(void) printf("Each input line should contain the following\n");
(void) printf("test parameters separated by a single space:\n");
(void) printf("- Padding mode flag. Use 1 for RC5_CBC_Pad, else
0.\n");
(void) printf("- Number of rounds for RC5.\n");
(void) printf("- Key bytes in hexadecimal. Two characters per
byte like '01'.\n");
(void) printf("- IV bytes in hexadecimal. Must be 16 hex
characters.\n");
(void) printf("- Plaintext bytes in hexadecimal.\n");
(void) printf("An end of file or format error terminates the
tester.\n");
(void) printf("\n");
}
/* Convert a buffer from ascii hex to bytes.
* Set pTo_length to the byte length of the result.
* Return 1 if everything went OK.
*/
int hex_to_bytes (from, to, pTo_length)
char *from, *to;
int *pTo_length;
{
char *pHex; /* Ptr to next hex character. */
char *pByte; /* Ptr to next resulting byte. */
int byte_length = 0;
int value;
pByte = to;
for (pHex = from ; *pHex != 0 ; pHex += 2) {
if (1 != sscanf(pHex, "%02x", &value))
return (0);
*pByte++ = ((char)(value & 0xFF));
byte_length++;
}
*pTo_length = byte_length;
return (1);
}
/* Convert a buffer from bytes to ascii hex.
* Return 1 if everything went OK.
*/
int bytes_to_hex (from, from_length, to)
char *from, *to;
int from_length;
{
char *pHex; /* Ptr to next hex character. */
char *pByte; /* Ptr to next resulting byte. */
int value;
pHex = to;
for (pByte = from ; from_length > 0 ; from_length--) {
value = *pByte++ & 0xFF;
(void) sprintf(pHex, "%02x", value);
pHex += 2;
}
return (1);
}
/* Return 1 if get a valid test vector. */
int get_test_vector(ptv)
test_vector *ptv;
{
if (1 != scanf("%d", &ptv->padding_mode))
return (0);
if (1 != scanf("%d", &ptv->rounds))
return (0);
if ((ptv->rounds < 0) || (MAX_ROUNDS < ptv->rounds))
return (0);
if (1 != scanf("%s", &ptv->keytext))
return (0);
if (1 != hex_to_bytes(ptv->keytext, ptv->key,
&ptv->key_length))
return (0);
if (1 != scanf("%s", &ptv->ivtext))
return (0);
if (1 != hex_to_bytes(ptv->ivtext, ptv->iv,
&ptv->iv_length))
return (0);
if (BLOCK_LENGTH != ptv->iv_length)
return (0);
if (1 != scanf("%s", &ptv->plaintext))
return (0);
if (1 != hex_to_bytes(ptv->plaintext, ptv->plain,
&ptv->plain_length))
return (0);
return (1);
}
void run_test (ptv)
test_vector *ptv;
{
rc5UserKey *pKey;
rc5CBCAlg *pAlg;
int numBytesOut;
pKey = RC5_Key_Create ();
RC5_Key_Set (pKey, ptv->key_length, ptv->key);
pAlg = RC5_CBC_Create (ptv->padding_mode,
ptv->rounds,
RC5_FIRST_VERSION,
BB,
ptv->iv);
(void) RC5_CBC_Encrypt_Init (pAlg, pKey);
ptv->cipher_length = 0;
(void) RC5_CBC_Encrypt_Update (pAlg,
ptv->plain_length, ptv->plain,
&(numBytesOut),
MAX_CIPHER_LENGTH - ptv->cipher_length,
&(ptv->cipher[ptv->cipher_length]));
ptv->cipher_length += numBytesOut;
(void) RC5_CBC_Encrypt_Final (pAlg,
&(numBytesOut),
MAX_CIPHER_LENGTH - ptv->cipher_length,
&(ptv->cipher[ptv->cipher_length]));
ptv->cipher_length += numBytesOut;
bytes_to_hex (ptv->cipher, ptv->cipher_length,
ptv->ciphertext);
RC5_Key_Destroy (pKey);
RC5_CBC_Destroy (pAlg);
}
void show_results (ptv)
test_vector *ptv;
{
if (ptv->padding_mode)
printf ("RC5_CBC_Pad ");
else
printf ("RC5_CBC ");
printf ("R = %2d ", ptv->rounds);
printf ("Key = %s ", ptv->keytext);
printf ("IV = %s ", ptv->ivtext);
printf ("P = %s ", ptv->plaintext);
printf ("C = %s", ptv->ciphertext);
printf ("\n");
}
int main(argc, argv)
int argc;
char *argv[];
{
test_vector tv;
test_vector *ptv = &tv;
show_banner();
while (get_test_vector(ptv)) {
run_test(ptv);
show_results(ptv);
}
return (0);
}
9.2 Test vectors ๐ฆ
this is the input file for the test program above ๐ซถ the outputs come in the next subsection, so no peeking yet ๐
0 00 00 0000000000000000 0000000000000000
0 00 00 0000000000000000 ffffffffffffffff
0 00 00 0000000000000001 0000000000000000
0 00 00 0000000000000000 0000000000000001
0 00 00 0102030405060708 1020304050607080
0 01 11 0000000000000000 0000000000000000
0 02 00 0000000000000000 0000000000000000
0 02 00000000 0000000000000000 0000000000000000
0 08 00 0000000000000000 0000000000000000
0 08 00 0102030405060708 1020304050607080
0 12 00 0102030405060708 1020304050607080
0 16 00 0102030405060708 1020304050607080
0 08 01020304 0000000000000000 ffffffffffffffff
0 12 01020304 0000000000000000 ffffffffffffffff
0 16 01020304 0000000000000000 ffffffffffffffff
0 12 0102030405060708 0000000000000000 ffffffffffffffff
0 08 0102030405060708 0102030405060708 1020304050607080
0 12 0102030405060708 0102030405060708 1020304050607080
0 16 0102030405060708 0102030405060708 1020304050607080
0 08 01020304050607081020304050607080
0102030405060708 1020304050607080
0 12 01020304050607081020304050607080
0102030405060708 1020304050607080
0 16 01020304050607081020304050607080
0102030405060708 1020304050607080
0 12 0102030405 0000000000000000 ffffffffffffffff
0 08 0102030405 0000000000000000 ffffffffffffffff
0 08 0102030405 7875dbf6738c6478 0808080808080808
1 08 0102030405 0000000000000000 ffffffffffffffff
0 08 0102030405 0000000000000000 0000000000000000
0 08 0102030405 7cb3f1df34f94811 1122334455667701
1 08 0102030405 0000000000000000
ffffffffffffffff7875dbf6738c647811223344556677
9.3 Test results ๐
this is what the test program spits out when you feed it the inputs from 9.2 ๐ โจ
RC5 CBC Tester.
Each input line should contain the following
test parameters separated by a single space:
- Padding mode flag. Use 1 for RC5_CBC_Pad, else 0.
- Number of rounds for RC5.
- Key bytes in hexadecimal. Two characters per byte
like '01'.
- IV bytes in hexadecimal. Must be 16 hex characters.
- Plaintext bytes in hexadecimal.
An end of file or format error terminates the tester.
RC5_CBC R = 0 Key = 00 IV = 0000000000000000
P = 0000000000000000 C = 7a7bba4d79111d1e
RC5_CBC R = 0 Key = 00 IV = 0000000000000000
P = ffffffffffffffff C = 797bba4d78111d1e
RC5_CBC R = 0 Key = 00 IV = 0000000000000001
P = 0000000000000000 C = 7a7bba4d79111d1f
RC5_CBC R = 0 Key = 00 IV = 0000000000000000
P = 0000000000000001 C = 7a7bba4d79111d1f
RC5_CBC R = 0 Key = 00 IV = 0102030405060708
P = 1020304050607080 C = 8b9ded91ce7794a6
RC5_CBC R = 1 Key = 11 IV = 0000000000000000
P = 0000000000000000 C = 2f759fe7ad86a378
RC5_CBC R = 2 Key = 00 IV = 0000000000000000
P = 0000000000000000 C = dca2694bf40e0788
RC5_CBC R = 2 Key = 00000000 IV = 0000000000000000
P = 0000000000000000 C = dca2694bf40e0788
RC5_CBC R = 8 Key = 00 IV = 0000000000000000
P = 0000000000000000 C = dcfe098577eca5ff
RC5_CBC R = 8 Key = 00 IV = 0102030405060708
P = 1020304050607080 C = 9646fb77638f9ca8
RC5_CBC R = 12 Key = 00 IV = 0102030405060708
P = 1020304050607080 C = b2b3209db6594da4
RC5_CBC R = 16 Key = 00 IV = 0102030405060708
P = 1020304050607080 C = 545f7f32a5fc3836
RC5_CBC R = 8 Key = 01020304 IV = 0000000000000000
P = ffffffffffffffff C = 8285e7c1b5bc7402
RC5_CBC R = 12 Key = 01020304 IV = 0000000000000000
P = ffffffffffffffff C = fc586f92f7080934
RC5_CBC R = 16 Key = 01020304 IV = 0000000000000000
P = ffffffffffffffff C = cf270ef9717ff7c4
RC5_CBC R = 12 Key = 0102030405060708 IV = 0000000000000000
P = ffffffffffffffff C = e493f1c1bb4d6e8c
RC5_CBC R = 8 Key = 0102030405060708 IV = 0102030405060708
P = 1020304050607080 C = 5c4c041e0f217ac3
RC5_CBC R = 12 Key = 0102030405060708 IV = 0102030405060708
P = 1020304050607080 C = 921f12485373b4f7
RC5_CBC R = 16 Key = 0102030405060708 IV = 0102030405060708
P = 1020304050607080 C = 5ba0ca6bbe7f5fad
RC5_CBC R = 8 Key = 01020304050607081020304050607080
IV = 0102030405060708
P = 1020304050607080 C = c533771cd0110e63
RC5_CBC R = 12 Key = 01020304050607081020304050607080
IV = 0102030405060708
P = 1020304050607080 C = 294ddb46b3278d60
RC5_CBC R = 16 Key = 01020304050607081020304050607080
IV = 0102030405060708
P = 1020304050607080 C = dad6bda9dfe8f7e8
RC5_CBC R = 12 Key = 0102030405 IV = 0000000000000000
P = ffffffffffffffff C = 97e0787837ed317f
RC5_CBC R = 8 Key = 0102030405 IV = 0000000000000000
P = ffffffffffffffff C = 7875dbf6738c6478
RC5_CBC R = 8 Key = 0102030405 IV = 7875dbf6738c6478
P = 0808080808080808 C = 8f34c3c681c99695
RC5_CBC_Pad R = 8 Key = 0102030405 IV = 0000000000000000
P = ffffffffffffffff C = 7875dbf6738c64788f34c3c681c99695
RC5_CBC R = 8 Key = 0102030405 IV = 0000000000000000
P = 0000000000000000 C = 7cb3f1df34f94811
RC5_CBC R = 8 Key = 0102030405 IV = 7cb3f1df34f94811
P = 1122334455667701 C = 7fd1a023a5bba217
RC5_CBC_Pad R = 8 Key = 0102030405 IV = 0000000000000000
P = ffffffffffffffff7875dbf6738c647811223344556677
C = 7875dbf6738c64787cb3f1df34f948117fd1a023a5bba217
10. Security Considerations ๐โจ
okay bestie, this is the serious part, so listen up ๐ RC5 is still pretty new, which means critical reviews are still happening right now. The good news is that its simple structure makes it easy to analyze, so hopefully it's easier to figure out how strong it actually is. And honestly? Reviews so far are very promising ๐
Early results [1] suggest that for RC5 with a 64 bit block size (32 bit word size), 12 rounds will suffice to resist linear and differential cyptanalysis. The 128 bit block version hasn't been studied as much as the 64 bit one, but it looks like 16 rounds would be an appropriate minimum ๐ฆ Block sizes less than 64 bits are academically interesting, but they should not be used for cryptographic security ๐ โโ๏ธ Greater security can be achieved by increasing the number of rounds, but that costs you throughput, so it's a trade-off ๐
The length of the secret key helps decide how well the cipher holds up against brute force key searching attacks. A key length of 128 bits should give adequate protection against brute force key searching by a well funded opponent for a couple decades [7] โจ For RC5 with 12 rounds, the key setup time and data encryption time are the same for all key lengths less than 832 bits, so there's no performance reason to pick short keys ๐ For larger keys, the key expansion step will run slower because the user key table, L, will be longer than the expanded key table, S. However, the encryption time stays unchanged, since it only depends on the number of rounds ๐ธ
To comply with export regulations it may be necessary to choose keys that only have 40 unknown bits. A poor way to do this would be to choose a simple 5 byte key. This should be avoided, because it would be easy for an opponent to pre-compute key searching information ๐ Another common mechanism is to pick a 128 bit key and publish the first 88 bits. This method reveals a large number of the entries in the user key table, L, and whether RC5 key expansion provides adequate security in this situation has not been studied, though it may be fine ๐คทโโ๏ธ A conservative way to conform to a 40 bit limitation is to pick a seed value of 128 bits, publish 88 bits of this seed, run the entire seed through a hash function like MD5 [4], and use the 128 bit output of the hash function as the RC5 key ๐ง
In the case of 40 unknown key bits with 88 known key bits (i.e., 88 salt bits) there should still be 12 or more rounds for the 64 bit block version of RC5, otherwise the value of adding salt bits to the key is likely to be lost. ๐ญ
The lifetime of the key also affects security. For high security applications, the key to any 64 bit block cipher should be changed after encrypting 2**32 blocks (2**64 blocks for a 128 bit block cipher). This helps guard against linear and differential cryptanalysis ๐ฅ For the case of 64 bit blocks, this rule would recommend changing the key after 2**40 (i.e. 10**12) bytes are encrypted. See Schneier [6] page 183 for further discussion ๐
11. ASN.1 Identifiers ๐งพ๐
For applications that use ASN.1 descriptions, it's necessary to define the algorithm identifier for these ciphers, along with their parameter block formats. The ASN.1 definition of an algorithm identifier already exists, and it's listed below for reference ๐
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}
The values for the algorithm field are:
RC5_CBC OBJECT IDENTIFIER ::=
{ iso (1) member-body (2) US (840) rsadsi (113549)
encryptionAlgorithm (3) RC5CBC (8) }
RC5_CBC_Pad OBJECT IDENTIFIER ::=
{ iso (1) member-body (2) US (840) rsadsi (113549)
encryptionAlgorithm (3) RC5CBCPAD (9) }
The structure of the parameters field for these algorithms is given below. NOTE: if the iv field is not included, then the initialization vector defaults to a block of zeros whose size depends on the blockSizeInBits field. โญ
RC5_CBC_Parameters ::= SEQUENCE {
version INTEGER (v1_0(16)),
rounds INTEGER (8..127),
blockSizeInBits INTEGER (64, 128),
iv OCTET STRING OPTIONAL
}
References ๐
[1] Kaliski, Burton S., and Yinqun Lisa Yin, "On Differential and Linear Cryptanalysis of the RC5 Encryption Algorithm", In Advances in Cryptology - Crypto '95, pages 171-184, Springer-Verlag, New York, 1995.
[2] Rivest, Ronald L., "The RC5 Encryption Algorithm", In Proceedings of the Second International Workshop on Fast Software Encryption, pages 86-96, Leuven Belgium, December 1994.
[3] Rivest, Ronald L., "RC5 Encryption Algorithm", In Dr. Dobbs Journal, number 226, pages 146-148, January 1995.
[4] Rivest, Ronald L., "The MD5 Message-Digest Algorithm", RFC 1321.
[5] RSA Laboratories, "Public Key Cryptography Standards (PKCS)", RSA Data Security Inc. See ftp.rsa.com.
[6] Schneier, Bruce, "Applied Cryptography", Second Edition, John Wiley and Sons, New York, 1996. Errata: on page 195, line 13, the reference number should be [402].
[7] Business Software Alliance, Matt Blaze et al., "Minimum Key Length for Symmetric Ciphers to Provide Adequate Commercial Security", http://www.bsa.org/bsa/cryptologists.html.
[8] RSA Data Security Inc., "RC5 Reference Code in C", See the web site: www.rsa.com, for availability. Not available with the first draft of this document.
Authors' Addresses ๐
Robert W. Baldwin
RSA Data Security, Inc.
100 Marine Parkway
Redwood City, CA 94065
Phone: (415) 595-8782
Fax: (415) 595-1873
EMail: baldwin@rsa.com, or baldwin@lcs.mit.edu
Ronald L. Rivest
Massachusetts Institute of Technology
Laboratory for Computer Science
NE43-324
545 Technology Square
Cambridge, MA 02139-1986
Phone: (617) 253-5880
EMail: rivest@theory.lcs.mit.edu
๐ฆ where this page comes from
- ietf: rfc2040 1996-10
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms