Gost28147Engine
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");
}
}
}