1 /**
2 * RC5
3 * 
4 * Copyright:
5 * (C) 1999-2007 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.block.rc5;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_RC5):
15 
16 import botan.utils.loadstor;
17 import botan.utils.rotate;
18 import botan.utils.parsing;
19 import std.algorithm;
20 import botan.block.block_cipher;
21 import std.conv : to;
22 import botan.utils.mem_ops;
23 
24 /**
25 * RC5
26 */
27 final class RC5 : BlockCipherFixedParams!(8, 1, 32), BlockCipher, SymmetricAlgorithm
28 {
29 public:
30     /*
31     * RC5 Encryption
32     */
33     override void encryptN(const(ubyte)* input, ubyte* output, size_t blocks)
34     {
35         foreach (size_t i; 0 .. blocks)
36         {
37             uint A = loadLittleEndian!uint(input, 0);
38             uint B = loadLittleEndian!uint(input, 1);
39             
40             A += m_S[0]; B += m_S[1];
41             for (size_t j = 0; j != m_rounds; j += 4)
42             {
43                 A = rotateLeft(A ^ B, B % 32) + m_S[2*j+2];
44                 B = rotateLeft(B ^ A, A % 32) + m_S[2*j+3];
45                 
46                 A = rotateLeft(A ^ B, B % 32) + m_S[2*j+4];
47                 B = rotateLeft(B ^ A, A % 32) + m_S[2*j+5];
48                 
49                 A = rotateLeft(A ^ B, B % 32) + m_S[2*j+6];
50                 B = rotateLeft(B ^ A, A % 32) + m_S[2*j+7];
51                 
52                 A = rotateLeft(A ^ B, B % 32) + m_S[2*j+8];
53                 B = rotateLeft(B ^ A, A % 32) + m_S[2*j+9];
54             }
55             
56             storeLittleEndian(output, A, B);
57             
58             input += BLOCK_SIZE;
59             output += BLOCK_SIZE;
60         }
61     }
62 
63     /*
64     * RC5 Decryption
65     */
66     override void decryptN(const(ubyte)* input, ubyte* output, size_t blocks)
67     {
68         foreach (size_t i; 0 .. blocks)
69         {
70             uint A = loadLittleEndian!uint(input, 0);
71             uint B = loadLittleEndian!uint(input, 1);
72             
73             for (size_t j = m_rounds; j != 0; j -= 4)
74             {
75                 B = rotateRight(B - m_S[2*j+1], A % 32) ^ A;
76                 A = rotateRight(A - m_S[2*j  ], B % 32) ^ B;
77                 
78                 B = rotateRight(B - m_S[2*j-1], A % 32) ^ A;
79                 A = rotateRight(A - m_S[2*j-2], B % 32) ^ B;
80                 
81                 B = rotateRight(B - m_S[2*j-3], A % 32) ^ A;
82                 A = rotateRight(A - m_S[2*j-4], B % 32) ^ B;
83                 
84                 B = rotateRight(B - m_S[2*j-5], A % 32) ^ A;
85                 A = rotateRight(A - m_S[2*j-6], B % 32) ^ B;
86             }
87             B -= m_S[1]; A -= m_S[0];
88             
89             storeLittleEndian(output, A, B);
90             
91             input += BLOCK_SIZE;
92             output += BLOCK_SIZE;
93         }
94     }
95 
96     override void clear()
97     {
98         zap(m_S);
99     }
100 
101     /*
102     * Return the name of this type
103     */
104     override @property string name() const
105     {
106         return "RC5(" ~ to!string(m_rounds) ~ ")";
107     }
108 
109     override @property size_t parallelism() const { return 1; }
110     override BlockCipher clone() const { return new RC5(m_rounds); }
111 
112     override size_t blockSize() const { return super.blockSize(); }
113     override KeyLengthSpecification keySpec() const { return super.keySpec(); }
114 
115     /**
116     * RC5 Constructor
117     * Params:
118     *  r = the number of RC5 rounds to run. Must be between
119     * 8 and 32 and a multiple of 4.
120     */
121     this(size_t r)
122     {
123         m_rounds = r;
124         if (m_rounds < 8 || m_rounds > 32 || (m_rounds % 4 != 0))
125             throw new InvalidArgument("RC5: Invalid number of rounds " ~
126                                        to!string(m_rounds));
127     }
128 protected:
129 
130     /*
131     * RC5 Key Schedule
132     */
133     override void keySchedule(const(ubyte)* key, size_t length)
134     {
135         m_S.resize(2*m_rounds + 2);
136         
137         const size_t WORD_KEYLENGTH = (((length - 1) / 4) + 1);
138         const size_t MIX_ROUNDS      = 3 * std.algorithm.max(WORD_KEYLENGTH, m_S.length);
139         
140         m_S[0] = 0xB7E15163;
141         foreach (size_t i; 1 .. m_S.length)
142             m_S[i] = m_S[i-1] + 0x9E3779B9;
143 
144         SecureVector!uint K = SecureVector!uint(8);
145         
146         for (int i = cast(int) length-1; i >= 0; --i)
147             K[i/4] = (K[i/4] << 8) + key[i];
148         
149         uint A = 0, B = 0;
150         
151         foreach (size_t i; 0 .. MIX_ROUNDS)
152         {
153             A = rotateLeft(m_S[i % m_S.length] + A + B, 3);
154             B = rotateLeft(K[i % WORD_KEYLENGTH] + A + B, (A + B) % 32);
155             m_S[i % m_S.length] = A;
156             K[i % WORD_KEYLENGTH] = B;
157         }
158     }
159 
160 
161     size_t m_rounds;
162     SecureVector!uint m_S;
163 }