📜 ⬆️ ⬇️

Once again about encryption GOST 28147-89

FTM has already talked about the implementation of this encryption algorithm: both in general and about the simple replacement mode . After studying the existing libraries and individual implementations of this GOST in C #, I decided to write my bicycle, first of all, for the sake of interest and experience. I would like to share the results of this work with a respected community.

GOST 28147-89 is a symmetric block encryption algorithm with a 256-bit key, operates with data blocks of 64 bits each.
One of its modes of operation, gamming with feedback, is the stream mode of the block cipher.

Algorithm Description


  1. The original message is divided into blocks of 64 bits.
  2. Each block XOR'om "superimposed" gamma, also a length of 64 bits
  3. Gamma is generated by encrypting a 64-bit “state” block using a key in the simple replacement mode
    • At the time of the beginning of the message encryption, the block is assumed to be equal to the sync send or initialization vector
    • In the next iteration, the encrypted block of text from the previous one is used instead of the sync package.

Please note that the above sequence of actions is valid for both encryption and decryption. The difference is where the encrypted text block comes from to process the next block, this is best seen in the picture:


')

Implementation


This algorithm was implemented in the form of a plug-in to the KeePass password manager.
Sources are available on GitHub.

Feedback Gamming


Below is a code snippet of a class that implements the standard ICryptoTransform interface, which actually performs cryptographic data transformation block by block. When creating an instance, the value of synchrumming is recorded in the _state attribute, then the next block of encrypted data is entered into it from the direction of work (encryption or decryption).

public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { byte[] dataBlock = new byte[inputCount]; byte[] gamma = new byte[GostECB.BlockSize]; byte[] result = new byte[inputCount]; Array.Copy(inputBuffer, inputOffset, dataBlock, 0, inputCount); gamma = GostECB.Process(_state, _key, GostECB.SBox_CryptoPro_A, true); result = XOr(dataBlock, gamma); Array.Copy(result, 0, outputBuffer, outputOffset, inputCount); Array.Copy(_encrypt ? result : dataBlock, _state, inputCount); return inputCount; } 

Simple replacement mode


GostECB.Process - implementation of the same GOST in the simple replacement mode, or “electronic code book”. A good description of the algorithm is in the corresponding section of the Wikipedia article , as well as in the article GOST 28147-89 (Part 2. Simple replacement mode) in Habrahabr.

The size of the snooze, gamma and “state” is 64 bytes, so encryption in the simple replacement mode can be considered within one block. However, there would be several - they would simply be encrypted in turn.

The source code of the GostECB.Process method
 public static byte[] Process(byte[] data, byte[] key, byte[][] sBox, bool encrypt) { Debug.Assert(data.Length == BlockSize, "BlockSize must be 64-bit long"); Debug.Assert(key.Length == KeyLength, "Key must be 256-bit long"); var a = BitConverter.ToUInt32(data, 0); var b = BitConverter.ToUInt32(data, 4); var subKeys = GetSubKeys(key); var result = new byte[8]; for (int i = 0; i < 32; i++) { var keyIndex = GetKeyIndex(i, encrypt); var subKey = subKeys[keyIndex]; var fValue = F(a, subKey, sBox); var round = b ^ fValue; if (i < 31) { b = a; a = round; } else { b = round; } } Array.Copy(BitConverter.GetBytes(a), 0, result, 0, 4); Array.Copy(BitConverter.GetBytes(b), 0, result, 4, 4); return result; } 


To work with 32-bit parts of the source block it is very convenient to use the uint type.
So, in the F () function, the addition modulo the key and the part of the block, as well as the cyclic shift by 11 bits, is written simply and concisely:

 private static uint F(uint block, uint subKey, byte[][] sBox) { block = (block + subKey) % uint.MaxValue; block = Substitute(block, sBox); block = (block << 11) | (block >> 21); return block; } 

The substitution method on S-blocks works with 4-bit pieces of a 32-bit sub-block, it is quite convenient to separate them with a bitwise shift and further multiplication by 0x0f :

 private static uint Substitute(uint value, byte[][] sBox) { byte index, sBlock; uint result = 0; for (int i = 0; i < 8; i++) { index = (byte)(value >> (4 * i) & 0x0f); sBlock = sBox[i][index]; result |= (uint)sBlock << (4 * i); } return result; } 

Encryption from decryption in simple replacement mode differs in the order in which keys are used. Actually, with reference to the gamma-linking mode with feedback, we do not need to decipher anything, however, for completeness of the implementation, this possibility can be foreseen:

 private static int GetKeyIndex(int i, bool encrypt) { return encrypt ? (i < 24) ? i % 8 : 7 - (i % 8) : (i < 8) ? i % 8 : 7 - (i % 8); } 

Sources


Source: https://habr.com/ru/post/256843/


All Articles