1 /**
2 * IDEA
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.idea;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_IDEA):
15 
16 import botan.block.block_cipher;
17 import botan.utils.loadstor;
18 import botan.utils.types;
19 import botan.utils.mem_ops;
20 
21 /**
22 * IDEA
23 */
24 class IDEA : BlockCipherFixedParams!(8, 16), BlockCipher, SymmetricAlgorithm
25 {
26 public:
27     /*
28     * IDEA Encryption
29     */
30     override void encryptN(const(ubyte)* input, ubyte* output, size_t blocks)
31     {
32         idea_op(input, output, blocks, m_EK.ptr);
33     }
34 
35     /*
36     * IDEA Decryption
37     */
38     override void decryptN(const(ubyte)* input, ubyte* output, size_t blocks)
39     {
40         idea_op(input, output, blocks, m_DK.ptr);
41     }
42 
43     override void clear()
44     {
45         zap(m_EK);
46         zap(m_DK);
47     }
48 
49     @property string name() const { return "IDEA"; }
50     override @property size_t parallelism() const { return 1; }
51     override BlockCipher clone() const { return new IDEA; }
52     override size_t blockSize() const { return super.blockSize(); }
53     override KeyLengthSpecification keySpec() const { return super.keySpec(); }
54 protected:
55     /**
56     * Returns: const reference to encryption subkeys
57     */
58     ref const(SecureVector!ushort) getEK() const { return m_EK; }
59 
60     /**
61     * Returns: const reference to decryption subkeys
62     */
63     ref const(SecureVector!ushort) getDK() const { return m_DK; }
64 
65     /*
66     * IDEA Key Schedule
67     */
68     override void keySchedule(const(ubyte)* key, size_t)
69     {
70         m_EK.resize(52);
71         m_DK.resize(52);
72         
73         foreach (size_t i; 0 .. 8)
74             m_EK[i] = loadBigEndian!ushort(key, i);
75         
76         for (size_t i = 1, j = 8, offset = 0; j != 52; i %= 8, ++i, ++j)
77         {
78             m_EK[i+7+offset] = cast(ushort)((m_EK[(i      % 8) + offset] << 9) |
79                                               (m_EK[((i+1) % 8) + offset] >> 7));
80             offset += (i == 8) ? 8 : 0;
81         }
82         
83         m_DK[51] = mul_inv(m_EK[3]);
84         m_DK[50] = -cast(int)m_EK[2];
85         m_DK[49] = -cast(int)m_EK[1];
86         m_DK[48] = mul_inv(m_EK[0]);
87         
88         for (size_t i = 1, j = 4, counter = 47; i != 8; ++i, j += 6)
89         {
90             m_DK[counter--] = m_EK[j+1];
91             m_DK[counter--] = m_EK[j];
92             m_DK[counter--] = mul_inv(m_EK[j+5]);
93             m_DK[counter--] = -cast(int)m_EK[j+3];
94             m_DK[counter--] = -cast(int)m_EK[j+4];
95             m_DK[counter--] = mul_inv(m_EK[j+2]);
96         }
97         
98         m_DK[5] = m_EK[47];
99         m_DK[4] = m_EK[46];
100         m_DK[3] = mul_inv(m_EK[51]);
101         m_DK[2] = -cast(int)m_EK[50];
102         m_DK[1] = -cast(int)m_EK[49];
103         m_DK[0] = mul_inv(m_EK[48]);
104     }
105 
106     SecureVector!ushort m_EK, m_DK;
107 }
108 
109 package:
110     
111 /*
112 * Multiplication modulo 65537
113 */
114 ushort mul(ushort x, ushort y) pure
115 {
116     const uint P = cast(uint)(x) * y;
117     
118     // P ? 0xFFFF : 0
119     const ushort P_mask = cast(const ushort)(!P - 1);
120     
121     const uint P_hi = P >> 16;
122     const uint P_lo = P & 0xFFFF;
123     
124     const ushort r_1 = cast(const ushort) ((P_lo - P_hi) + (P_lo < P_hi));
125     const ushort r_2 = cast(const ushort) (1 - x - y);
126     
127     return cast(const ushort) ((r_1 & P_mask) | (r_2 & ~cast(int)P_mask));
128 }
129 
130 /*
131 * Find multiplicative inverses modulo 65537
132 *
133 * 65537 is prime; thus Fermat's little theorem tells us that
134 * x^65537 == x modulo 65537, which means
135 * x^(65537-2) == x^-1 modulo 65537 since
136 * x^(65537-2) * x == 1 mod 65537
137 *
138 * Do the exponentiation with a basic square and multiply: all bits are
139 * of exponent are 1 so we always multiply
140 */
141 ushort mul_inv(ushort x) pure
142 {
143     ushort y = x;
144     
145     foreach (size_t i; 0 .. 15)
146     {
147         y = mul(y, y); // square
148         y = mul(y, x);
149     }
150     
151     return y;
152 }
153 
154 /**
155 * IDEA is involutional, depending only on the key schedule
156 */
157 void idea_op(const(ubyte)* input, ubyte* output, size_t blocks, in const(ushort)* K)
158 {
159     __gshared immutable size_t BLOCK_SIZE = 8;
160     
161     foreach (size_t i; 0 .. blocks)
162     {
163         ushort X1 = loadBigEndian!ushort(input, 0);
164         ushort X2 = loadBigEndian!ushort(input, 1);
165         ushort X3 = loadBigEndian!ushort(input, 2);
166         ushort X4 = loadBigEndian!ushort(input, 3);
167         
168         foreach (size_t j; 0 .. 8)
169         {
170             X1 = mul(X1, K[6*j+0]);
171             X2 += K[6*j+1];
172             X3 += K[6*j+2];
173             X4 = mul(X4, K[6*j+3]);
174             
175             ushort T0 = X3;
176             X3 = mul(X3 ^ X1, K[6*j+4]);
177             
178             ushort T1 = X2;
179             X2 = mul(cast(ushort) ((X2 ^ X4) + X3), K[6*j+5]);
180             X3 += X2;
181             
182             X1 ^= X2;
183             X4 ^= X3;
184             X2 ^= T0;
185             X3 ^= T1;
186         }
187         
188         X1  = mul(X1, K[48]);
189         X2 += K[50];
190         X3 += K[49];
191         X4  = mul(X4, K[51]);
192         
193         storeBigEndian(output, X1, X3, X2, X4);
194         
195         input += BLOCK_SIZE;
196         output += BLOCK_SIZE;
197     }
198 }