
in bits . RC5 encrypts in two word blocks; valid values are 16, 32, and 64. This value is recommended to be taken equal to the machine word. For example for 32-bit machines
= 32 and therefore the block size will be 64 bits
- integer from 0 to 255 inclusive. If set to 0, encryption will not be performed.
in bytes - an integer from 0 to 255 inclusive
;def __init__(self, w, R, key): self.w = w self.R = R self.key = key self.T = 2 * (R + 1) self.w4 = w // 4 self.w8 = w // 8 self.mod = 2 ** self.w self.mask = self.mod - 1 self.b = len(key) self.__keyAlign() self.__keyExtend() self.__shuffle()
, we supplement it with zero bytes to the nearest multiple
. After that, the key is copied to the array.
where
. Simply put, we copy the key in blocks of
bytes (2, 4, 8 for values
16, 32, 64 respectively) into an array
.
and key value
we'll get
and
(0 means zero byte). def __keyAlign(self): if self.b == 0: # self.c = 1 elif self.b % self.w8: # w / 8 self.key += b'\x00' * (self.w8 - self.b % self.w8) # \x00 self.b = len(self.key) self.c = self.b // self.w8 else: self.c = self.b // self.w8 L = [0] * self.c for i in range(self.b - 1, -1, -1): # L L[i // self.w8] = (L[i // self.w8] << 8) + self.key[i] self.L = L
and
according to the following formulas:
,
- rounding function to the nearest inappropriate
- Euler number
- golden ratio
:




,
where![S [0] = Pw](https://habrastorage.org/files/412/214/c3b/412214c3bc3346a2a1a93a62eb4257ed.gif)
![S [i + 1] = S [i] + Qw](https://habrastorage.org/files/8ba/b2a/76e/8bab2a76ef2c417f8f640af2c57e0180.gif)
def __const(self): # if self.W == 16: return (0xB7E1, 0x9E37) # P Q elif self.W == 32: return (0xB7E15163, 0x9E3779B9) elif self.W == 64: return (0xB7E151628AED2A6B, 0x9E3779B97F4A7C15) def __keyExtend(self): # S P, Q = self.__const() self.S = [(P + i * Q) % self.mod for i in range(self.T)] ![A = S [i] = (S [i] + A + A) <<< 3](https://habrastorage.org/files/2c3/679/2ab/2c36792abd0643d0993a70bbbecba30c.gif)
![B = L [j] = (L [j] + A + B) <<< (A + B)](https://habrastorage.org/files/5eb/110/1f6/5eb1101f6b844bd689f89e7b59b6796d.gif)

where
- temporary variables, initial values are 0
- arrays obtained in the previous steps
defined as 
def __shuffle(self): i, j, A, B = 0, 0, 0, 0 for k in range(3 * max(self.c, self.T)): A = self.S[i] = self.__lshift((self.S[i] + A + B), 3) B = self.L[j] = self.__lshift((self.L[j] + A + B), A + B) i = (i + 1) % self.T j = (j + 1) % self.c ![A = ((A + B) <<< B) + S [2 * r] mod2 ^ w](https://habrastorage.org/files/062/12e/cda/06212ecda1674f018670146136481463.gif)
,
- extended key fragment
- cyclic shift operation on
bits left![A = (A + S [0]) mod2 ^ w](https://habrastorage.org/files/3ef/39f/3d0/3ef39f3d0e98473da28752fd8850aa44.gif)
![B = (B + S [1]) mod2 ^ w](https://habrastorage.org/files/57f/7b1/a8f/57f7b1a8f3e8495ea9de91b0467b94ff.gif)
def encryptBlock(self, data): A = int.from_bytes(data[:self.w8], byteorder='little') B = int.from_bytes(data[self.w8:], byteorder='little') A = (A + self.S[0]) % self.mod B = (B + self.S[1]) % self.mod for i in range(1, self.R + 1): A = (self.__lshift((A ^ B), B) + self.S[2 * i]) % self.mod B = (self.__lshift((A ^ B), A) + self.S[2 * i + 1]) % self.mod return (A.to_bytes(self.w8, byteorder='little') def encryptFile(self, inpFileName, outFileName): # with open(inpFileName, 'rb') as inp, open(outFileName, 'wb') as out: run = True while run: text = inp.read(self.w4) if not text: break if len(text) != self.w4: text = text.ljust(self.w4, b'\x00') # , , run = False text = self.encryptBlock(text) out.write(text) ![B = (((B-S [2 * r + 1]) mod2 ^ w) >>> A) + A](https://habrastorage.org/files/255/df6/edb/255df6edbdd24c7ab507969c618719d2.gif)
,
- cyclic right shift operation
and ending with a unit.![B = (B-S [1]) mod2 ^ w](https://habrastorage.org/files/b57/61d/ab0/b5761dab08b54b67b8e1cad5c2432b4c.gif)
![A = (A-S [0]) mod2 ^ w](https://habrastorage.org/files/2b2/755/9df/2b27559df7cf4e839ab3200800381b59.gif)
def decryptBlock(self, data): A = int.from_bytes(data[:self.w8], byteorder='little') B = int.from_bytes(data[self.w8:], byteorder='little') for i in range(self.R, 0, -1): B = self.__rshift(B - self.S[2 * i + 1], A) ^ A A = self.__rshift(A - self.S[2 * i], B) ^ B B = (B - self.S[1]) % self.mod A = (A - self.S[0]) % self.mod return (A.to_bytes(self.w8, byteorder='little') + B.to_bytes(self.w8, byteorder='little')) def decryptFile(self, inpFileName, outFileName): with open(inpFileName, 'rb') as inp, open(outFileName, 'wb') as out: run = True while run: text = inp.read(self.w4) if not text: break if len(text) != self.w4: run = False text = self.decryptBlock(text) if not run: text = text.rstrip(b'\x00') # b'\x00' out.write(text)
as well as shifts by a variable number of bits. The last of the operations is presented by the author of the algorithm as a revolutionary solution that was not used in earlier encryption algorithms (prior to the RC5 algorithm, these were used only in the Madryga algorithm, which was not widely used) - a shift by a variable number of bits is a very simple operation, but significantly complicates the differential and linear cryptanalysis of the algorithm. The simplicity of the algorithm can be considered as its important advantage - a simple algorithm is easier to implement and easier to analyze for possible vulnerabilities.
selected clear texts for a successful attack. When using 18-20 (and more) rounds instead of 12, it is almost impossible to open the algorithm using differential cryptanalysis.Source: https://habr.com/ru/post/267295/
All Articles