<PackageReference Include="BouncyCastle.Cryptography" Version="2.3.1" />

Gost28147Engine

public class Gost28147Engine : IBlockCipher
using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; using System.Collections.Generic; namespace Org.BouncyCastle.Crypto.Engines { public class Gost28147Engine : IBlockCipher { private const int BlockSize = 8; private int[] workingKey; private bool forEncryption; private byte[] S = Sbox_Default; private static readonly byte[] Sbox_Default; private static readonly byte[] ESbox_Test; private static readonly byte[] ESbox_A; private static readonly byte[] ESbox_B; private static readonly byte[] ESbox_C; private static readonly byte[] ESbox_D; private static readonly byte[] DSbox_Test; private static readonly byte[] DSbox_A; private static readonly Dictionary<string, byte[]> m_sBoxes; public virtual string AlgorithmName => "Gost28147"; static Gost28147Engine() { Sbox_Default = new byte[128] { 4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3, 14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9, 5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11, 7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3, 6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2, 4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14, 13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12, 1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12 }; ESbox_Test = new byte[128] { 4, 2, 15, 5, 9, 1, 0, 8, 14, 3, 11, 12, 13, 7, 10, 6, 12, 9, 15, 14, 8, 1, 3, 10, 2, 7, 4, 13, 6, 0, 11, 5, 13, 8, 14, 12, 7, 3, 9, 10, 1, 5, 2, 4, 6, 15, 0, 11, 14, 9, 11, 2, 5, 15, 7, 1, 0, 13, 12, 6, 10, 4, 3, 8, 3, 14, 5, 9, 6, 8, 0, 13, 10, 11, 7, 12, 2, 1, 15, 4, 8, 15, 6, 11, 1, 9, 12, 5, 13, 3, 7, 10, 0, 14, 2, 4, 9, 11, 12, 0, 3, 6, 7, 5, 4, 8, 14, 15, 1, 10, 2, 13, 12, 6, 5, 2, 11, 0, 9, 13, 3, 14, 7, 10, 15, 4, 1, 8 }; ESbox_A = new byte[128] { 9, 6, 3, 2, 8, 11, 1, 7, 10, 4, 14, 15, 12, 0, 13, 5, 3, 7, 14, 9, 8, 10, 15, 0, 5, 2, 6, 12, 11, 4, 13, 1, 14, 4, 6, 2, 11, 3, 13, 8, 12, 15, 5, 10, 0, 7, 1, 9, 14, 7, 10, 12, 13, 1, 3, 9, 0, 2, 11, 4, 15, 8, 5, 6, 11, 5, 1, 9, 8, 13, 15, 0, 14, 4, 2, 3, 12, 7, 10, 6, 3, 10, 13, 12, 1, 2, 0, 11, 7, 5, 9, 4, 8, 15, 14, 6, 1, 13, 2, 9, 7, 10, 6, 0, 8, 12, 4, 5, 15, 3, 11, 14, 11, 10, 15, 5, 0, 12, 14, 8, 6, 2, 3, 9, 1, 7, 13, 4 }; ESbox_B = new byte[128] { 8, 4, 11, 1, 3, 5, 0, 9, 2, 14, 10, 12, 13, 6, 7, 15, 0, 1, 2, 10, 4, 13, 5, 12, 9, 7, 3, 15, 11, 8, 6, 14, 14, 12, 0, 10, 9, 2, 13, 11, 7, 5, 8, 15, 3, 6, 1, 4, 7, 5, 0, 13, 11, 6, 1, 2, 3, 10, 12, 15, 4, 14, 9, 8, 2, 7, 12, 15, 9, 5, 10, 11, 1, 4, 0, 13, 6, 8, 14, 3, 8, 3, 2, 6, 4, 13, 14, 11, 12, 1, 7, 15, 10, 0, 9, 5, 5, 2, 10, 11, 9, 1, 12, 3, 7, 4, 13, 0, 6, 15, 8, 14, 0, 4, 11, 14, 8, 3, 7, 1, 10, 2, 9, 6, 15, 13, 5, 12 }; ESbox_C = new byte[128] { 1, 11, 12, 2, 9, 13, 0, 15, 4, 5, 8, 14, 10, 7, 6, 3, 0, 1, 7, 13, 11, 4, 5, 2, 8, 14, 15, 12, 9, 10, 6, 3, 8, 2, 5, 0, 4, 9, 15, 10, 3, 7, 12, 13, 6, 14, 1, 11, 3, 6, 0, 1, 5, 13, 10, 8, 11, 2, 9, 7, 14, 15, 12, 4, 8, 13, 11, 0, 4, 5, 1, 2, 9, 3, 12, 14, 6, 15, 10, 7, 12, 9, 11, 1, 8, 14, 2, 4, 7, 3, 6, 5, 10, 0, 15, 13, 10, 9, 6, 8, 13, 14, 2, 0, 15, 3, 5, 11, 4, 1, 12, 7, 7, 4, 0, 5, 10, 2, 15, 14, 12, 6, 1, 11, 13, 9, 3, 8 }; ESbox_D = new byte[128] { 15, 12, 2, 10, 6, 4, 5, 0, 7, 9, 14, 13, 1, 11, 8, 3, 11, 6, 3, 4, 12, 15, 14, 2, 7, 13, 8, 0, 5, 10, 9, 1, 1, 12, 11, 0, 15, 14, 6, 5, 10, 13, 4, 8, 9, 3, 7, 2, 1, 5, 14, 12, 10, 7, 0, 13, 6, 2, 11, 4, 9, 3, 15, 8, 0, 12, 8, 9, 13, 2, 10, 11, 7, 3, 6, 5, 4, 14, 15, 1, 8, 0, 15, 3, 2, 5, 14, 11, 1, 10, 4, 7, 12, 9, 13, 6, 3, 0, 6, 15, 1, 14, 9, 2, 13, 8, 12, 4, 11, 10, 5, 7, 1, 10, 6, 8, 15, 11, 0, 4, 12, 3, 5, 9, 7, 13, 2, 14 }; DSbox_Test = new byte[128] { 4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3, 14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9, 5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11, 7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3, 6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2, 4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14, 13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12, 1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12 }; DSbox_A = new byte[128] { 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15, 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8, 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13, 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3, 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5, 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3, 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11, 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 }; m_sBoxes = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase); AddSBox("Default", Sbox_Default); AddSBox("E-TEST", ESbox_Test); AddSBox("E-A", ESbox_A); AddSBox("E-B", ESbox_B); AddSBox("E-C", ESbox_C); AddSBox("E-D", ESbox_D); AddSBox("D-TEST", DSbox_Test); AddSBox("D-A", DSbox_A); } private static void AddSBox(string sBoxName, byte[] sBox) { m_sBoxes.Add(sBoxName, sBox); } public virtual void Init(bool forEncryption, ICipherParameters parameters) { ParametersWithSBox parametersWithSBox = parameters as ParametersWithSBox; if (parametersWithSBox != null) { byte[] sBox = parametersWithSBox.GetSBox(); if (sBox.Length != Sbox_Default.Length) throw new ArgumentException("invalid S-box passed to GOST28147 init"); S = Arrays.Clone(sBox); if (parametersWithSBox.Parameters != null) workingKey = GenerateWorkingKey(forEncryption, ((KeyParameter)parametersWithSBox.Parameters).GetKey()); } else { KeyParameter keyParameter = parameters as KeyParameter; if (keyParameter != null) workingKey = GenerateWorkingKey(forEncryption, keyParameter.GetKey()); else if (parameters != null) { throw new ArgumentException("invalid parameter passed to Gost28147 init - " + Platform.GetTypeName(parameters)); } } } public virtual int GetBlockSize() { return 8; } public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff) { if (workingKey == null) throw new InvalidOperationException("Gost28147 engine not initialised"); Check.DataLength(input, inOff, 8, "input buffer too short"); Check.OutputLength(output, outOff, 8, "output buffer too short"); Gost28147Func(workingKey, input.AsSpan(inOff), output.AsSpan(outOff)); return 8; } public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) { if (workingKey == null) throw new InvalidOperationException("Gost28147 engine not initialised"); Check.DataLength(input, 8, "input buffer too short"); Check.OutputLength(output, 8, "output buffer too short"); Gost28147Func(workingKey, input, output); return 8; } private int[] GenerateWorkingKey(bool forEncryption, byte[] userKey) { this.forEncryption = forEncryption; if (userKey.Length != 32) throw new ArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!"); int[] array = new int[8]; for (int i = 0; i != 8; i++) { array[i] = (int)Pack.LE_To_UInt32(userKey, i * 4); } return array; } private int Gost28147_mainStep(int n1, int key) { int num = key + n1; int num2 = S[num & 15] + (S[16 + ((num >> 4) & 15)] << 4) + (S[32 + ((num >> 8) & 15)] << 8) + (S[48 + ((num >> 12) & 15)] << 12) + (S[64 + ((num >> 16) & 15)] << 16) + (S[80 + ((num >> 20) & 15)] << 20) + (S[96 + ((num >> 24) & 15)] << 24) + (S[112 + ((num >> 28) & 15)] << 28); int num3 = num2 << 11; int num4 = (int)((uint)num2 >> 21); return num3 | num4; } private void Gost28147Func(int[] workingKey, ReadOnlySpan<byte> input, Span<byte> output) { int num = (int)Pack.LE_To_UInt32(input); int num2 = (int)Pack.LE_To_UInt32(input.Slice(4, input.Length - 4)); if (forEncryption) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 8; j++) { int num3 = num; int num4 = Gost28147_mainStep(num, workingKey[j]); num = (num2 ^ num4); num2 = num3; } } for (int num5 = 7; num5 > 0; num5--) { int num6 = num; num = (num2 ^ Gost28147_mainStep(num, workingKey[num5])); num2 = num6; } } else { for (int k = 0; k < 8; k++) { int num7 = num; num = (num2 ^ Gost28147_mainStep(num, workingKey[k])); num2 = num7; } for (int l = 0; l < 3; l++) { int num8 = 7; while (num8 >= 0 && (l != 2 || num8 != 0)) { int num9 = num; num = (num2 ^ Gost28147_mainStep(num, workingKey[num8])); num2 = num9; num8--; } } } num2 ^= Gost28147_mainStep(num, workingKey[0]); Pack.UInt32_To_LE((uint)num, output); Pack.UInt32_To_LE((uint)num2, output.Slice(4, output.Length - 4)); } public static byte[] GetSBox(string sBoxName) { if (!m_sBoxes.TryGetValue(sBoxName, out byte[] value)) throw new ArgumentException("Unknown S-Box - possible types: \"Default\", \"E-Test\", \"E-A\", \"E-B\", \"E-C\", \"E-D\", \"D-Test\", \"D-A\"."); return Arrays.Clone(value); } public static string GetSBoxName(byte[] sBox) { foreach (KeyValuePair<string, byte[]> sBox2 in m_sBoxes) { if (Arrays.AreEqual(sBox2.Value, sBox)) return sBox2.Key; } throw new ArgumentException("SBOX provided did not map to a known one"); } } }