Šifra RC6 byla představena v roce 1997, autorem byl především Ronald Rivest (tvůrce asymetrické šifry RSA). Představuje blokovou symetrickou šifru pracující na principu Feistelovy šifry. Obvyklý zápis blíže specifikující šifru má tvar RC6-w/r/b. Kde w značí bitovou velikost jednoho ze čtyř vstupních registrů (měla by mít hodnotu některé mocniny 2), vstupní slovo se rozdělí do 4 registrů velikosti w. Symbol r značí počet šifrovacích rund (viz Feistelova šifra). A dále b značí počet bajtů (nikoli bitů!) klíče.
Je rozdělen do dvou kroků. V prvním kroku se vygenerují klíče (key setup) používané v jednotlivých rundách algoritmu. V dalším kroku se šifruje / dešifruje dle schématu níže.
Uživatel musí dodat klíč o velikosti b bajtů, kde 0 ≤ b ≤ 255, zde kterého se generuje pole klíčů pro jednotlivé rundy S, velikosti (2r+4). V rámci algoritmu jsou jednotlivé bajty klíče ve formě little-endian přeneseny do pole L o velikosti c (c je alespoň jedna). Algoritmus pro generování klíče dále pracuje s hodnotami Pw a Qw, kde Pw se dopočítává ve formě Pw = NejblizsiVetsiCiRovneLicheCislo((e-2)⋅2w), kde e je základ přirozeného logaritmu. Qw = NejblizsiVetsiCiRovneLicheCislo((φ-1)⋅2w), kde φ je hodnota zlatého řezu. Zbytek algoritmu pro generování klíče se řídí následujícím předpisem:
S[0] = Pw
for i = 1 to (2r + 3) do
S[i] = S[i - 1] + Qw
A = B = i = j = 0
v = 3 x max{c, 2r + 4}
for s = 1 to v do
{
A = S[i] = (S[i] + A + B) <<< 3
B = L[j] = (L[j] + A + B) <<< (A + B)
i = (i + 1) mod (2r + 4)
j = (j + 1) mod c
}
Sčítání, označené symbolem + v algoritmu, představuje součet čísel a, b v následující formě: (a+b) mod 2w (stejně tak i odčítání). Násobení označené symbolem x představuje součin čísel a, b v následující formě: (a⋅b) mod 2w. Logaritmus o základu 2 je označen jako log2.
Šifrování sleduje následující schéma (ze kterého je jasně vidět souvislost se šifrou Feistel):
Celý algoritmus šifrování by se pomocí pseudokódu nechal zapsat jako:
B = B + S[0]
D = D + S[1]
for i = 1 to r do
{
t = (B x (2B + 1)) <<< log2 w
u = (D x (2D + 1)) <<< log2 w
A = ((A t) <<< u) + S[2i]
C = ((C u) <<< t) + S[2i+ 1]
(A,B,C,D) = (B,C,D,A)
}
A = A + S[2r + 2]
C = C + S[2r + 3]
Sčítání, označené symbolem + v algoritmu, představuje součet čísel a, b v následující formě: (a+b) mod 2w (stejně tak i odčítání). Násobení označené symbolem x představuje součin čísel a, b v následující formě: (a⋅b) mod 2w. Logaritmus o základu 2 je označen jako log2.
Probíhá překlopením šifrování (jedná se stále o Feistelovu šifru). Základní algoritmus má následující tvar:
C = C - S[2r + 3]
A = A - S[2r + 2]
for i = r downto 1 do
{
(A,B,C,D) = (D,A,B,C)
u = (D x (2D + 1)) <<< log2 w
t = (B x (2B + 1)) <<< log2 w
C = ((C - S[2i + 1]) >>> t) u
A = ((A - S[2i]) >>> u) t
}
D = D - S[1]
B = B - S[0]
Sčítání, označené symbolem + v algoritmu, představuje součet čísel a, b v následující formě: (a+b) mod 2w (stejně tak i odčítání). Násobení označené symbolem x představuje součin čísel a, b v následující formě: (a⋅b) mod 2w. Logaritmus o základu 2 je označen jako log2.
Pro názornou ukázku toho, jak se algoritmus může realizovat, nechť poslouží následující ukázka v jazyce Java (zdroj ZDE)
package rc6;
public class RC6 {
private static int w = 32, r = 20;
private static int P32 = 0xb7e15163, Q32 = 0x9e3779b9;
private static int[] SubKey;
// This method return the Encryption block
private static byte[] GenerateEncryptionBlocks(byte[] plaintext, byte[] key) {
byte[] CompleteBlock = new byte[16];
SubKey = GenerateSubkeys(key);
int[] dataBlock = new int[plaintext.length / 4];
int registerA, registerB, registerC, registerD;
//fill array initially to all zero
for (int i = 0; i < dataBlock.length; i++) {
dataBlock[i] = 0;
}
int index = 0;
for (int i = 0; i < dataBlock.length; i++) {
dataBlock[i] = (plaintext[index++] & 0xFF) | ((plaintext[index++] & 0xFF) << 8) | ((plaintext[index++] & 0xFF) << 16) | ((plaintext[index++] & 0xFF) << 24);
}
registerA = dataBlock[0];
registerB = dataBlock[1];
registerC = dataBlock[2];
registerD = dataBlock[3];
registerB = registerB + SubKey[0];
registerD = registerD + SubKey[1];
for (int i = 1; i <= r; i++) {
//log 32=5
int resultLeftRotate_B = ((registerB * (2 * registerB + 1)) << 5) | ((registerB * (2 * registerB + 1)) >>> 27);
int resultLeftRotate_D = ((registerD * (2 * registerD + 1)) << 5) | ((registerD * (2 * registerD + 1)) >>> 27);
registerA = (((registerA ^ resultLeftRotate_B) << resultLeftRotate_D) | ((registerA ^ resultLeftRotate_B) >>> 32 - resultLeftRotate_D)) + SubKey[2 * i];
registerC = (((registerC ^ resultLeftRotate_D) << resultLeftRotate_B) | ((registerC ^ resultLeftRotate_D) >>> 32 - resultLeftRotate_B)) + SubKey[2 * i + 1];
int swap = registerA;
registerA = registerB;
registerB = registerC;
registerC = registerD;
registerD = swap;
}
registerA = registerA + SubKey[2 * r + 2];
registerC = registerC + SubKey[2 * r + 3];
dataBlock[0] = registerA;
dataBlock[1] = registerB;
dataBlock[2] = registerC;
dataBlock[3] = registerD;
for (int i = 0; i < CompleteBlock.length; i++) {
CompleteBlock[i] = (byte) ((dataBlock[i / 4] >>> (i % 4) * 8) & 0xff);
}
return CompleteBlock;
}
// This method return the Decryption block
private static byte[] GenerateDecryptionBlock(byte[] cipherText, byte[] key) {
byte[] CompleteBlock = new byte[16];
SubKey = GenerateSubkeys(key);
int[] dataBlock = new int[cipherText.length / 4];
int registerA, registerB, registerC, registerD;
//fill array initially to all zero
for (int i = 0; i < dataBlock.length; i++) {
dataBlock[i] = 0;
}
int index = 0;
for (int i = 0; i < dataBlock.length; i++) {
dataBlock[i] = (cipherText[index++] & 0xff) | ((cipherText[index++] & 0xff) << 8) | ((cipherText[index++] & 0xff) << 16) | ((cipherText[index++] & 0xff) << 24);
}
registerA = dataBlock[0];
registerB = dataBlock[1];
registerC = dataBlock[2];
registerD = dataBlock[3];
registerC = registerC - SubKey[2 * r + 3];
registerA = registerA - SubKey[2 * r + 2];
for (int i = r; i >= 1; i--) {
//log 32=5
//f (A;B;C;D)=(D;A;B;C)
int swap = registerD;
registerD = registerC;
registerC = registerB;
registerB = registerA;
registerA = swap;
int resultLeftRotate_D = ((registerD * (2 * registerD + 1)) << 5) | ((registerD * (2 * registerD + 1)) >>> 27);
int resultLeftRotate_B = ((registerB * (2 * registerB + 1)) << 5) | ((registerB * (2 * registerB + 1)) >>> 27);
registerC = (((registerC - SubKey[2 * i + 1]) >>> resultLeftRotate_B) | ((registerC - SubKey[2 * i + 1]) << 32 - resultLeftRotate_B)) ^ resultLeftRotate_D;
registerA = (((registerA - SubKey[2 * i]) >>> resultLeftRotate_D) | ((registerA - SubKey[2 * i]) << 32 - resultLeftRotate_D)) ^ resultLeftRotate_B;
}
registerD = registerD - SubKey[1];
registerB = registerB - SubKey[0];
dataBlock[0] = registerA;
dataBlock[1] = registerB;
dataBlock[2] = registerC;
dataBlock[3] = registerD;
for (int i = 0; i < CompleteBlock.length; i++) {
CompleteBlock[i] = (byte) ((dataBlock[i / 4] >>> (i % 4) * 8) & 0xff);
}
return CompleteBlock;
}
private static int[] GenerateSubkeys(byte[] inputKey) {
int c = inputKey.length / 4;
int[] L = new int[c];
for (int i = 0; i < L.length; i++) {
L[i] = 0;
}
for (int i = 0, j = 0; i < c; i++) {
L[i] = ((inputKey[j++] & 0xFF)) | ((inputKey[j++] & 0xFF) << 8) | ((inputKey[j++] & 0xFF) << 16) | ((inputKey[j++] & 0xFF) << 24);
}
int[] S = new int[2 * r + 4];
S[0] = P32;
for (int i = 1; i < 2 * r + 4; i++) {
S[i] = S[i - 1] + Q32;
}
int A = 0;
int B = 0;
int I = 0;
int J = 0;
int v = 3 * Math.max(c, 2 * r + 4);
for (int i = 0; i < v; i++) {
A = S[I] = ((S[I] + A + B) << 3) | ((S[I] + A + B) >>> 29);
int x = A + B;
B = L[J] = ((L[J] + A + B) << x) | ((L[J] + A + B) >>> 32 - x);
I = (I + 1) % (2 * r + 4);
J = (J + 1) % c;
}
return S;
}
public static void main(String[] args) {
String plain = "Velka Cinska zed";
String key = "Bazilisek zeleny";
byte[] c = GenerateEncryptionBlocks(plain.getBytes(), key.getBytes());
byte[] m = GenerateDecryptionBlock(c, key.getBytes());
String cipher = new String(m);
System.out.println(cipher);
}
}
MORGEN, Morgan. RC6 : The Simple Cipher [online]. 2004, , 11 [cit. 2017-08-09]. Dostupné z: http://studylib.net/doc/7714382/the-rc6-algorithm-is-a-block-cipher-that-was-one-of-the-f..