The GOST 28147 Cipher

GOST (Gosudarstvennyy Standard — State Standard) 28147–89 was developed as a symmetric key standard in Russia in 1989 and is typically…

The GOST 28147 Cipher

GOST (Gosudarstvennyy Standard — State Standard) 28147–89 was developed as a symmetric key standard in Russia in 1989 and is typically used for secure government and military communications. It typically uses a 256-bit key and uses 32-bit blocks, and where each block is substituted using another 32-bit block in a substitution table [here]:

If you are into cybersecurity, you will know about the famous hashing methods of MD5, SHA1 and SHA2 (SHA-256). There’s a chance you might know about the Gost hash, but do you know about the Streebog hashing method? And, so, NIST defines many of the standards that we use in cybersecurity. This includes AES and SHA-3. Along with this, they have also defined the FIPS standards, such as FIPS 186–5, which defines the standard for digital signatures.

EASC (Euro-Asian Council for Standardization, Metrology and Certification) is part of the Commonwealth of Independent States (CIS) and which includes Russia, Belarus, Moldova, Kazakhstan, Azerbaijan, Armenia, Kyrgyzstan, Uzbekistan, Tajikistan, Georgia, and Turkmenistan. The first standard — GOST (ГОСТ — GOvernment STandard) — was published in 1968, and it has since been recognised by the ISO. Ukraine removed the GOST standard in 2015.

GOST cipher

The GOST block cipher uses a 64-bit block and is defined in RFC 5830 [here]. It was basically an alternative to the DES method and uses a Feistel network of 32 rounds. The GOST hashing method is then based on this block cipher. A new standard for GOST defines a 128-bit block cipher (Kuznyechik).

With the GOST hashing method, we split the message (M) into 256-bit blocks (m_1 to m_n), and pad the last block with zero bits. Each block then uses a step hash, and where H_1 is 256 bits of zero:

Figure here

First we create a folder named “bc_gost, and then go into that folder. We can create a Dotnet console project for .NET 8.0 with:

dotnet new console --framework net8.0

This produces a Csproject file of:

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

We then add the latest Bouncy Castle library:

dotnet add package BouncyCastle.Cryptography --version 2.2.1

The following is the coding [here]:

namespace Gost
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
class Program
{

static void Main(string[] args)
{



var msg="Hello";

var iv="00112233445566778899AABBCCDDEEFF00";
var size=128;
var mode="CBC";
if (args.Length >0) msg=args[0];
if (args.Length >1) iv=args[1];
if (args.Length >2) size=Convert.ToInt32(args[2]);
if (args.Length >3) mode=args[3];



try {
var plainTextData=System.Text.Encoding.UTF8.GetBytes(msg);
var cipher = new Org.BouncyCastle.Crypto.Engines.Gost28147Engine();

byte[] nonce = new byte[8];
Array.Copy(Convert.FromHexString(iv), nonce, 8);

PaddedBufferedBlockCipher cipherMode = new PaddedBufferedBlockCipher(new CbcBlockCipher(cipher), new Pkcs7Padding());

if (mode=="ECB") cipherMode = new PaddedBufferedBlockCipher(new EcbBlockCipher (cipher), new Pkcs7Padding());
else if (mode=="CFB") cipherMode = new PaddedBufferedBlockCipher(new CfbBlockCipher (cipher,128 ), new Pkcs7Padding());

CipherKeyGenerator keyGen = new CipherKeyGenerator();
keyGen.Init(new KeyGenerationParameters(new SecureRandom(), size));
KeyParameter keyParam = keyGen.GenerateKeyParameter();

cipherMode.Init(true,keyParam);
int outputSize = cipherMode.GetOutputSize(plainTextData.Length);
byte[] cipherTextData = new byte[outputSize];
int result = cipherMode.ProcessBytes(plainTextData, 0, plainTextData.Length, cipherTextData, 0);
cipherMode.DoFinal(cipherTextData, result);
var rtn = cipherTextData;

// Decrypt
cipherMode.Init(false,keyParam);
outputSize = cipherMode.GetOutputSize(cipherTextData.Length);
plainTextData = new byte[outputSize];
result = cipherMode.ProcessBytes(cipherTextData, 0, cipherTextData.Length,plainTextData, 0);

cipherMode.DoFinal(plainTextData, result);
var pln=plainTextData;

Console.WriteLine("=== {0} ==",cipher.AlgorithmName);
Console.WriteLine("Message:\t\t{0}",msg);
Console.WriteLine("Block size:\t\t{0} bits",cipher.GetBlockSize()*8);
Console.WriteLine("Mode:\t\t\t{0}",mode);
Console.WriteLine("IV:\t\t\t{0}",iv);
Console.WriteLine("Key size:\t\t{0} bits",size);
Console.WriteLine("Key:\t\t\t{0} [{1}]",Convert.ToHexString(keyParam.GetKey()),Convert.ToBase64String(keyParam.GetKey()));

Console.WriteLine("\nCipher (hex):\t\t{0}",Convert.ToHexString(rtn));
Console.WriteLine("Cipher (Base64):\t{0}",Convert.ToBase64String(rtn));
Console.WriteLine("\nPlain:\t\t\t{0}",System.Text.Encoding.ASCII.GetString(pln).TrimEnd('\0'));

} catch (Exception e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
}

}

A sample run is [here]:

=== Gost28147 ==
Message: Hello 123
Block size: 64 bits
Mode: ECB
IV: 00112233445566778899AABBCCDDEEFF00
Key size: 256 bits
Key: EC3EF7194383699CE1FCA5C4AAB24CD6B1FB1C5ED954091E9A036339098D6AC6 [7D73GUODaZzh/KXEqrJM1rH7HF7ZVAkemgNjOQmNasY=]

Cipher (hex): 011CB7EB865359800A9C8803B163AA0E
Cipher (Base64): ARy364ZTWYAKnIgDsWOqDg==

Plain: Hello 123

Conclusions

And, so, NIST is not the only agency setting standards for cryptography.