Once again about encryption GOST 28147-89
- Tutorial
FTM has already talked about the implementation of this encryption algorithm : 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 bike, first of all, for the sake of interest and experience. I would like to share the results of this work with a reputable community.
GOST 28147-89 - a symmetric block encryption algorithm with a 256-bit key, operates with 64-bit data blocks.
One of the modes of its operation, gamming with feedback, is a streaming mode of block cipher.
Please note that the above sequence of actions is valid for both encryption and decryption. The difference is where the encrypted block of text comes from to process the next block, this is best seen in the picture:

This algorithm was implemented in the form of a plugin to the KeePass password manager.
Sources are available on GitHub.
Below is a code snippet of a class that implements the standard ICryptoTransform interface , which actually performs cryptographic data conversion block by block. When creating an instance, the value of the sync message is recorded in the _state attribute , and then from the direction of work (encryption or decryption), the next block of encrypted data is entered into it.
GostECB.Process - the implementation of the same GOST in a 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) on Habrahabr.
The size of the package, gamma, and “state” is 64 bytes, so encryption in simple replacement mode can be considered within one block. However, there would be several - they would simply be encrypted in turn.
It is very convenient to use the uint type to work with 32-bit parts of the source block .
So, in the F () function , modulo addition of the key and part of the block, as well as a cyclic shift of 11 bits, is written simply and concisely:
The S-block substitution method works with 4-bit pieces of a 32-bit sub-block, it is quite convenient to separate them with a bit shift and further multiplication by 0x0f :
Encryption from decryption in simple replacement mode differs in the order in which keys are used. In fact, in relation to the gamming mode with feedback, we do not need to decrypt anything, however, for the sake of completeness, this possibility can also be envisaged:
GOST 28147-89 - a symmetric block encryption algorithm with a 256-bit key, operates with 64-bit data blocks.
One of the modes of its operation, gamming with feedback, is a streaming mode of block cipher.
Algorithm description
- The original message is broken into blocks of 64 bits
- On each block XOR'om "overlays" gamma, also a length of 64 bits
- The gamma is formed by encrypting a 64-bit “state” block using a key in simple replacement mode
- At the moment the message is encrypted, the block is taken equal to the sync packet or the initialization vector
- In the next iteration, instead of sync sending, an encrypted block of text from the previous one is used
Please note that the above sequence of actions is valid for both encryption and decryption. The difference is where the encrypted block of text comes from to process the next block, this is best seen in the picture:

Implementation
This algorithm was implemented in the form of a plugin 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 conversion block by block. When creating an instance, the value of the sync message is recorded in the _state attribute , and then from the direction of work (encryption or decryption), the next block of encrypted data is entered into it.
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;
}
Easy Replace Mode
GostECB.Process - the implementation of the same GOST in a 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) on Habrahabr.
The size of the package, gamma, and “state” is 64 bytes, so encryption in simple replacement mode can be considered within one block. However, there would be several - they would simply be encrypted in turn.
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;
}
It is very convenient to use the uint type to work with 32-bit parts of the source block .
So, in the F () function , modulo addition of the key and part of the block, as well as a cyclic shift of 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 S-block substitution method works with 4-bit pieces of a 32-bit sub-block, it is quite convenient to separate them with a bit 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. In fact, in relation to the gamming mode with feedback, we do not need to decrypt anything, however, for the sake of completeness, this possibility can also be envisaged:
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
- GOST 28147-89 article on Wikipedia
- Description of the operating modes of block ciphers in the English-language Wikipedia Block cipher mode of operation
- Sources of implementation of GOST 28147-89 Gromila / CryptoAlgorithms on GitHub
- Sources of implementation of GOST 28147-89 sftp / gost28147 on GitHub