1 /** 2 * GOST 28147-89 3 * 4 * Copyright: 5 * (C) 1999-2009 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.gost_28147; 12 13 import botan.constants; 14 static if (BOTAN_HAS_GOST_28147_89): 15 16 import botan.block.block_cipher; 17 import botan.utils.loadstor; 18 import botan.utils.rotate; 19 import botan.utils.exceptn; 20 import botan.utils.get_byte; 21 import botan.utils.mem_ops; 22 import std.format : format; 23 24 /** 25 * The GOST 28147-89 block cipher uses a set of 4 bit Sboxes, however 26 * the standard does not actually define these Sboxes; they are 27 * considered a local configuration issue. Several different sets are 28 * used. 29 */ 30 final class GOST_28147_89_Params 31 { 32 public: 33 /** 34 * Params: 35 * row = the row 36 * col = the column 37 * Returns: sbox entry at this row/column 38 */ 39 ubyte sboxEntry(size_t row, size_t col) const 40 { 41 ubyte x = m_sboxes[4 * col + (row / 2)]; 42 43 return (row % 2 == 0) ? (x >> 4) : (x & 0x0F); 44 } 45 46 /** 47 * Returns: name of this parameter set 48 */ 49 string paramName() const { return m_name; } 50 51 /** 52 * Default GOST parameters are the ones given in GOST R 34.11 for 53 * testing purposes; these sboxes are also used by Crypto++, and, 54 * at least according to Wikipedia, the Central Bank of Russian 55 * Federation 56 * 57 * Params: 58 * name = of the parameter set 59 */ 60 this(in string name = "R3411_94_TestParam") 61 { 62 m_name = name; 63 // Encoded in the packed fromat from RFC 4357 64 65 // GostR3411_94_TestParamSet (OID 1.2.643.2.2.31.0) 66 __gshared immutable ubyte[64] GOST_R_3411_TEST_PARAMS = [ 67 0x4E, 0x57, 0x64, 0xD1, 0xAB, 0x8D, 0xCB, 0xBF, 0x94, 0x1A, 0x7A, 68 0x4D, 0x2C, 0xD1, 0x10, 0x10, 0xD6, 0xA0, 0x57, 0x35, 0x8D, 0x38, 69 0xF2, 0xF7, 0x0F, 0x49, 0xD1, 0x5A, 0xEA, 0x2F, 0x8D, 0x94, 0x62, 70 0xEE, 0x43, 0x09, 0xB3, 0xF4, 0xA6, 0xA2, 0x18, 0xC6, 0x98, 0xE3, 71 0xC1, 0x7C, 0xE5, 0x7E, 0x70, 0x6B, 0x09, 0x66, 0xF7, 0x02, 0x3C, 72 0x8B, 0x55, 0x95, 0xBF, 0x28, 0x39, 0xB3, 0x2E, 0xCC ]; 73 74 // GostR3411-94-CryptoProParamSet (OID 1.2.643.2.2.31.1) 75 __gshared immutable ubyte[64] GOST_R_3411_CRYPTOPRO_PARAMS = [ 76 0xA5, 0x74, 0x77, 0xD1, 0x4F, 0xFA, 0x66, 0xE3, 0x54, 0xC7, 0x42, 77 0x4A, 0x60, 0xEC, 0xB4, 0x19, 0x82, 0x90, 0x9D, 0x75, 0x1D, 0x4F, 78 0xC9, 0x0B, 0x3B, 0x12, 0x2F, 0x54, 0x79, 0x08, 0xA0, 0xAF, 0xD1, 79 0x3E, 0x1A, 0x38, 0xC7, 0xB1, 0x81, 0xC6, 0xE6, 0x56, 0x05, 0x87, 80 0x03, 0x25, 0xEB, 0xFE, 0x9C, 0x6D, 0xF8, 0x6D, 0x2E, 0xAB, 0xDE, 81 0x20, 0xBA, 0x89, 0x3C, 0x92, 0xF8, 0xD3, 0x53, 0xBC ]; 82 83 if (m_name == "R3411_94_TestParam") 84 m_sboxes = GOST_R_3411_TEST_PARAMS; 85 else if (m_name == "R3411_CryptoPro") 86 m_sboxes = GOST_R_3411_CRYPTOPRO_PARAMS; 87 else 88 throw new InvalidArgument("GOST_28147_89_Params: Unknown " ~ m_name); 89 } 90 private: 91 const ubyte[64] m_sboxes; 92 string m_name; 93 } 94 95 /** 96 * GOST 28147-89 97 */ 98 final class GOST_28147_89 : BlockCipherFixedParams!(8, 32), BlockCipher, SymmetricAlgorithm 99 { 100 public: 101 102 /* 103 * GOST Encryption 104 */ 105 override void encryptN(const(ubyte)* input, ubyte* output, size_t blocks) 106 { 107 foreach (size_t i; 0 .. blocks) 108 { 109 uint N1 = loadLittleEndian!uint(input, 0); 110 uint N2 = loadLittleEndian!uint(input, 1); 111 112 foreach (size_t j; 0 .. 3) 113 { 114 mixin(GOST_2ROUND!(N1, N2, 0, 1)); 115 mixin(GOST_2ROUND!(N1, N2, 2, 3)); 116 mixin(GOST_2ROUND!(N1, N2, 4, 5)); 117 mixin(GOST_2ROUND!(N1, N2, 6, 7)); 118 } 119 120 mixin(GOST_2ROUND!(N1, N2, 7, 6)); 121 mixin(GOST_2ROUND!(N1, N2, 5, 4)); 122 mixin(GOST_2ROUND!(N1, N2, 3, 2)); 123 mixin(GOST_2ROUND!(N1, N2, 1, 0)); 124 125 storeLittleEndian(output, N2, N1); 126 127 input += BLOCK_SIZE; 128 output += BLOCK_SIZE; 129 } 130 } 131 132 /* 133 * GOST Decryption 134 */ 135 override void decryptN(const(ubyte)* input, ubyte* output, size_t blocks) 136 { 137 foreach (size_t i; 0 .. blocks) 138 { 139 uint N1 = loadLittleEndian!uint(input, 0); 140 uint N2 = loadLittleEndian!uint(input, 1); 141 142 mixin(GOST_2ROUND!(N1, N2, 0, 1)); 143 mixin(GOST_2ROUND!(N1, N2, 2, 3)); 144 mixin(GOST_2ROUND!(N1, N2, 4, 5)); 145 mixin(GOST_2ROUND!(N1, N2, 6, 7)); 146 147 foreach (size_t j; 0 .. 3) 148 { 149 mixin(GOST_2ROUND!(N1, N2, 7, 6)); 150 mixin(GOST_2ROUND!(N1, N2, 5, 4)); 151 mixin(GOST_2ROUND!(N1, N2, 3, 2)); 152 mixin(GOST_2ROUND!(N1, N2, 1, 0)); 153 } 154 155 storeLittleEndian(output, N2, N1); 156 input += BLOCK_SIZE; 157 output += BLOCK_SIZE; 158 } 159 } 160 161 override void clear() 162 { 163 zap(m_EK); 164 } 165 166 @property string name() const 167 { 168 /* 169 'Guess' the right name for the sbox on the basis of the values. 170 This would need to be updated if support for other sbox parameters 171 is added. Preferably, we would just store the string value in the 172 constructor, but can't break binary compat. 173 */ 174 string sbox_name = ""; 175 if (m_SBOX[0] == 0x00072000) 176 sbox_name = "R3411_94_TestParam"; 177 else if (m_SBOX[0] == 0x0002D000) 178 sbox_name = "R3411_CryptoPro"; 179 else 180 throw new InternalError("GOST-28147 unrecognized sbox value"); 181 182 return "GOST-28147-89(" ~ sbox_name ~ ")"; 183 } 184 185 override @property size_t parallelism() const { return 1; } 186 override BlockCipher clone() const { return new GOST_28147_89(m_SBOX); } 187 override size_t blockSize() const { return super.blockSize(); } 188 override KeyLengthSpecification keySpec() const { return super.keySpec(); } 189 190 this(in string params) { 191 this(scoped!GOST_28147_89_Params(params).Scoped_payload); 192 } 193 194 /** 195 * Params: 196 * param = the sbox parameters to use 197 */ 198 this(in GOST_28147_89_Params param) 199 { 200 m_SBOX = Vector!uint(1024); 201 // Convert the parallel 4x4 sboxes into larger word-based sboxes 202 foreach (size_t i; 0 .. 4) 203 { 204 foreach (size_t j; 0 .. 256) 205 { 206 const uint T = (param.sboxEntry(2*i , j % 16)) | 207 (param.sboxEntry(2*i+1, j / 16) << 4); 208 m_SBOX[256*i+j] = rotateLeft(T, (11+8*i) % 32); 209 } 210 } 211 } 212 protected: 213 this(const ref Vector!uint other_SBOX) { 214 m_SBOX = other_SBOX.dup; 215 m_EK = 8; 216 } 217 218 /* 219 * GOST Key Schedule 220 */ 221 override void keySchedule(const(ubyte)* key, size_t) 222 { 223 m_EK.resize(8); 224 foreach (size_t i; 0 .. 8) 225 m_EK[i] = loadLittleEndian!uint(key, i); 226 } 227 228 /* 229 * The sbox is not secret, this is just a larger expansion of it 230 * which we generate at runtime for faster execution 231 */ 232 Vector!uint m_SBOX; 233 234 SecureVector!uint m_EK; 235 } 236 237 protected: 238 239 /* 240 * Two rounds of GOST 241 */ 242 enum string GOST_2ROUND(alias N1, alias N2, ubyte R1, ubyte R2) = q{ 243 { 244 uint T0 = %1$s + m_EK[%3$s]; 245 N2 ^= m_SBOX[get_byte(3, T0)] | 246 m_SBOX[get_byte(2, T0)+256] | 247 m_SBOX[get_byte(1, T0)+512] | 248 m_SBOX[get_byte(0, T0)+768]; 249 250 uint T1 = %2$s + m_EK[%4$s]; 251 N1 ^= m_SBOX[get_byte(3, T1)] | 252 m_SBOX[get_byte(2, T1)+256] | 253 m_SBOX[get_byte(1, T1)+512] | 254 m_SBOX[get_byte(0, T1)+768]; 255 } 256 }.format(__traits(identifier, N1), __traits(identifier, N2), R1, R2);