Jedná se mimořádně bezpečnou a přitom jednoduchou blokovou šifru. Předností je, že může pracovat s různými počty rund, různou délkou klíče a vstupního slova.

Bloková šifra RC6

Š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.

Princip

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.

Generování klíč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ě: (ab) mod 2w. Logaritmus o základu 2 je označen jako log2.

Šifrování

Šifrování sleduje následující schéma (ze kterého je jasně vidět souvislost se šifrou Feistel):

Schéma šifry RC6

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ě: (ab) mod 2w. Logaritmus o základu 2 je označen jako log2.

Dešifrování

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ě: (ab) mod 2w. Logaritmus o základu 2 je označen jako log2.

Programování

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);
    }
}
    

Zdroje:

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..