CamelliaLightEngine
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Engines
{
public class CamelliaLightEngine : IBlockCipher
{
private const int BLOCK_SIZE = 16;
private bool initialised;
private bool _keyis128;
private uint[] subkey = new uint[96];
private uint[] kw = new uint[8];
private uint[] ke = new uint[12];
private static readonly uint[] SIGMA = new uint[12] {
2694735487,
1003262091,
3061508184,
1286239154,
3337565999,
3914302142,
1426019237,
4057165596,
283453434,
3731369245,
2958461122,
3018244605
};
private static readonly byte[] SBOX1 = new byte[256] {
112,
130,
44,
236,
179,
39,
192,
229,
228,
133,
87,
53,
234,
12,
174,
65,
35,
239,
107,
147,
69,
25,
165,
33,
237,
14,
79,
78,
29,
101,
146,
189,
134,
184,
175,
143,
124,
235,
31,
206,
62,
48,
220,
95,
94,
197,
11,
26,
166,
225,
57,
202,
213,
71,
93,
61,
217,
1,
90,
214,
81,
86,
108,
77,
139,
13,
154,
102,
251,
204,
176,
45,
116,
18,
43,
32,
240,
177,
132,
153,
223,
76,
203,
194,
52,
126,
118,
5,
109,
183,
169,
49,
209,
23,
4,
215,
20,
88,
58,
97,
222,
27,
17,
28,
50,
15,
156,
22,
83,
24,
242,
34,
254,
68,
207,
178,
195,
181,
122,
145,
36,
8,
232,
168,
96,
252,
105,
80,
170,
208,
160,
125,
161,
137,
98,
151,
84,
91,
30,
149,
224,
byte.MaxValue,
100,
210,
16,
196,
0,
72,
163,
247,
117,
219,
138,
3,
230,
218,
9,
63,
221,
148,
135,
92,
131,
2,
205,
74,
144,
51,
115,
103,
246,
243,
157,
127,
191,
226,
82,
155,
216,
38,
200,
55,
198,
59,
129,
150,
111,
75,
19,
190,
99,
46,
233,
121,
167,
140,
159,
110,
188,
142,
41,
245,
249,
182,
47,
253,
180,
89,
120,
152,
6,
106,
231,
70,
113,
186,
212,
37,
171,
66,
136,
162,
141,
250,
114,
7,
185,
85,
248,
238,
172,
10,
54,
73,
42,
104,
60,
56,
241,
164,
64,
40,
211,
123,
187,
201,
67,
193,
21,
227,
173,
244,
119,
199,
128,
158
};
public virtual string AlgorithmName => "Camellia";
private static uint rightRotate(uint x, int s)
{
return (x >> s) + (x << 32 - s);
}
private static uint leftRotate(uint x, int s)
{
return (x << s) + (x >> 32 - s);
}
private static void roldq(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
{
ko[ooff] = ((ki[ioff] << rot) | (ki[1 + ioff] >> 32 - rot));
ko[1 + ooff] = ((ki[1 + ioff] << rot) | (ki[2 + ioff] >> 32 - rot));
ko[2 + ooff] = ((ki[2 + ioff] << rot) | (ki[3 + ioff] >> 32 - rot));
ko[3 + ooff] = ((ki[3 + ioff] << rot) | (ki[ioff] >> 32 - rot));
ki[ioff] = ko[ooff];
ki[1 + ioff] = ko[1 + ooff];
ki[2 + ioff] = ko[2 + ooff];
ki[3 + ioff] = ko[3 + ooff];
}
private static void decroldq(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
{
ko[2 + ooff] = ((ki[ioff] << rot) | (ki[1 + ioff] >> 32 - rot));
ko[3 + ooff] = ((ki[1 + ioff] << rot) | (ki[2 + ioff] >> 32 - rot));
ko[ooff] = ((ki[2 + ioff] << rot) | (ki[3 + ioff] >> 32 - rot));
ko[1 + ooff] = ((ki[3 + ioff] << rot) | (ki[ioff] >> 32 - rot));
ki[ioff] = ko[2 + ooff];
ki[1 + ioff] = ko[3 + ooff];
ki[2 + ioff] = ko[ooff];
ki[3 + ioff] = ko[1 + ooff];
}
private static void roldqo32(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
{
ko[ooff] = ((ki[1 + ioff] << rot - 32) | (ki[2 + ioff] >> 64 - rot));
ko[1 + ooff] = ((ki[2 + ioff] << rot - 32) | (ki[3 + ioff] >> 64 - rot));
ko[2 + ooff] = ((ki[3 + ioff] << rot - 32) | (ki[ioff] >> 64 - rot));
ko[3 + ooff] = ((ki[ioff] << rot - 32) | (ki[1 + ioff] >> 64 - rot));
ki[ioff] = ko[ooff];
ki[1 + ioff] = ko[1 + ooff];
ki[2 + ioff] = ko[2 + ooff];
ki[3 + ioff] = ko[3 + ooff];
}
private static void decroldqo32(int rot, uint[] ki, int ioff, uint[] ko, int ooff)
{
ko[2 + ooff] = ((ki[1 + ioff] << rot - 32) | (ki[2 + ioff] >> 64 - rot));
ko[3 + ooff] = ((ki[2 + ioff] << rot - 32) | (ki[3 + ioff] >> 64 - rot));
ko[ooff] = ((ki[3 + ioff] << rot - 32) | (ki[ioff] >> 64 - rot));
ko[1 + ooff] = ((ki[ioff] << rot - 32) | (ki[1 + ioff] >> 64 - rot));
ki[ioff] = ko[2 + ooff];
ki[1 + ioff] = ko[3 + ooff];
ki[2 + ioff] = ko[ooff];
ki[3 + ioff] = ko[1 + ooff];
}
private byte lRot8(byte v, int rot)
{
return (byte)((v << rot) | (int)((uint)v >> 8 - rot));
}
private uint sbox2(int x)
{
return lRot8(SBOX1[x], 1);
}
private uint sbox3(int x)
{
return lRot8(SBOX1[x], 7);
}
private uint sbox4(int x)
{
return SBOX1[lRot8((byte)x, 1)];
}
private void camelliaF2(uint[] s, uint[] skey, int keyoff)
{
uint num = s[0] ^ skey[keyoff];
uint num2 = sbox4((byte)num);
num2 |= sbox3((byte)(num >> 8)) << 8;
num2 |= sbox2((byte)(num >> 16)) << 16;
num2 = (uint)((int)num2 | (SBOX1[(byte)(num >> 24)] << 24));
uint num3 = s[1] ^ skey[1 + keyoff];
uint num4 = SBOX1[(byte)num3];
num4 |= sbox4((byte)(num3 >> 8)) << 8;
num4 |= sbox3((byte)(num3 >> 16)) << 16;
num4 |= sbox2((byte)(num3 >> 24)) << 24;
num4 = leftRotate(num4, 8);
num2 ^= num4;
num4 = (leftRotate(num4, 8) ^ num2);
num2 = (rightRotate(num2, 8) ^ num4);
s[2] ^= (leftRotate(num4, 16) ^ num2);
s[3] ^= leftRotate(num2, 8);
num = (s[2] ^ skey[2 + keyoff]);
num2 = sbox4((byte)num);
num2 |= sbox3((byte)(num >> 8)) << 8;
num2 |= sbox2((byte)(num >> 16)) << 16;
num2 = (uint)((int)num2 | (SBOX1[(byte)(num >> 24)] << 24));
num3 = (s[3] ^ skey[3 + keyoff]);
num4 = SBOX1[(byte)num3];
num4 |= sbox4((byte)(num3 >> 8)) << 8;
num4 |= sbox3((byte)(num3 >> 16)) << 16;
num4 |= sbox2((byte)(num3 >> 24)) << 24;
num4 = leftRotate(num4, 8);
num2 ^= num4;
num4 = (leftRotate(num4, 8) ^ num2);
num2 = (rightRotate(num2, 8) ^ num4);
s[0] ^= (leftRotate(num4, 16) ^ num2);
s[1] ^= leftRotate(num2, 8);
}
private void camelliaFLs(uint[] s, uint[] fkey, int keyoff)
{
s[1] ^= leftRotate(s[0] & fkey[keyoff], 1);
s[0] ^= (fkey[1 + keyoff] | s[1]);
s[2] ^= (fkey[3 + keyoff] | s[3]);
s[3] ^= leftRotate(fkey[2 + keyoff] & s[2], 1);
}
private void setKey(bool forEncryption, byte[] key)
{
uint[] array = new uint[8];
uint[] array2 = new uint[4];
uint[] array3 = new uint[4];
uint[] array4 = new uint[4];
switch (key.LongLength) {
case 16:
_keyis128 = true;
Pack.BE_To_UInt32(key, 0, array, 0, 4);
array[4] = (array[5] = (array[6] = (array[7] = 0)));
break;
case 24:
Pack.BE_To_UInt32(key, 0, array, 0, 6);
array[6] = ~array[4];
array[7] = ~array[5];
_keyis128 = false;
break;
case 32:
Pack.BE_To_UInt32(key, 0, array, 0, 8);
_keyis128 = false;
break;
default:
throw new ArgumentException("key sizes are only 16/24/32 bytes.");
}
for (int i = 0; i < 4; i++) {
array2[i] = (array[i] ^ array[i + 4]);
}
camelliaF2(array2, SIGMA, 0);
for (int j = 0; j < 4; j++) {
array2[j] ^= array[j];
}
camelliaF2(array2, SIGMA, 4);
if (_keyis128) {
if (forEncryption) {
kw[0] = array[0];
kw[1] = array[1];
kw[2] = array[2];
kw[3] = array[3];
roldq(15, array, 0, subkey, 4);
roldq(30, array, 0, subkey, 12);
roldq(15, array, 0, array4, 0);
subkey[18] = array4[2];
subkey[19] = array4[3];
roldq(17, array, 0, ke, 4);
roldq(17, array, 0, subkey, 24);
roldq(17, array, 0, subkey, 32);
subkey[0] = array2[0];
subkey[1] = array2[1];
subkey[2] = array2[2];
subkey[3] = array2[3];
roldq(15, array2, 0, subkey, 8);
roldq(15, array2, 0, ke, 0);
roldq(15, array2, 0, array4, 0);
subkey[16] = array4[0];
subkey[17] = array4[1];
roldq(15, array2, 0, subkey, 20);
roldqo32(34, array2, 0, subkey, 28);
roldq(17, array2, 0, kw, 4);
} else {
kw[4] = array[0];
kw[5] = array[1];
kw[6] = array[2];
kw[7] = array[3];
decroldq(15, array, 0, subkey, 28);
decroldq(30, array, 0, subkey, 20);
decroldq(15, array, 0, array4, 0);
subkey[16] = array4[0];
subkey[17] = array4[1];
decroldq(17, array, 0, ke, 0);
decroldq(17, array, 0, subkey, 8);
decroldq(17, array, 0, subkey, 0);
subkey[34] = array2[0];
subkey[35] = array2[1];
subkey[32] = array2[2];
subkey[33] = array2[3];
decroldq(15, array2, 0, subkey, 24);
decroldq(15, array2, 0, ke, 4);
decroldq(15, array2, 0, array4, 0);
subkey[18] = array4[2];
subkey[19] = array4[3];
decroldq(15, array2, 0, subkey, 12);
decroldqo32(34, array2, 0, subkey, 4);
roldq(17, array2, 0, kw, 0);
}
} else {
for (int k = 0; k < 4; k++) {
array3[k] = (array2[k] ^ array[k + 4]);
}
camelliaF2(array3, SIGMA, 8);
if (forEncryption) {
kw[0] = array[0];
kw[1] = array[1];
kw[2] = array[2];
kw[3] = array[3];
roldqo32(45, array, 0, subkey, 16);
roldq(15, array, 0, ke, 4);
roldq(17, array, 0, subkey, 32);
roldqo32(34, array, 0, subkey, 44);
roldq(15, array, 4, subkey, 4);
roldq(15, array, 4, ke, 0);
roldq(30, array, 4, subkey, 24);
roldqo32(34, array, 4, subkey, 36);
roldq(15, array2, 0, subkey, 8);
roldq(30, array2, 0, subkey, 20);
ke[8] = array2[1];
ke[9] = array2[2];
ke[10] = array2[3];
ke[11] = array2[0];
roldqo32(49, array2, 0, subkey, 40);
subkey[0] = array3[0];
subkey[1] = array3[1];
subkey[2] = array3[2];
subkey[3] = array3[3];
roldq(30, array3, 0, subkey, 12);
roldq(30, array3, 0, subkey, 28);
roldqo32(51, array3, 0, kw, 4);
} else {
kw[4] = array[0];
kw[5] = array[1];
kw[6] = array[2];
kw[7] = array[3];
decroldqo32(45, array, 0, subkey, 28);
decroldq(15, array, 0, ke, 4);
decroldq(17, array, 0, subkey, 12);
decroldqo32(34, array, 0, subkey, 0);
decroldq(15, array, 4, subkey, 40);
decroldq(15, array, 4, ke, 8);
decroldq(30, array, 4, subkey, 20);
decroldqo32(34, array, 4, subkey, 8);
decroldq(15, array2, 0, subkey, 36);
decroldq(30, array2, 0, subkey, 24);
ke[2] = array2[1];
ke[3] = array2[2];
ke[0] = array2[3];
ke[1] = array2[0];
decroldqo32(49, array2, 0, subkey, 4);
subkey[46] = array3[0];
subkey[47] = array3[1];
subkey[44] = array3[2];
subkey[45] = array3[3];
decroldq(30, array3, 0, subkey, 32);
decroldq(30, array3, 0, subkey, 16);
roldqo32(51, array3, 0, kw, 0);
}
}
}
private int ProcessBlock128(ReadOnlySpan<byte> input, Span<byte> output)
{
uint[] array = new uint[4];
Pack.BE_To_UInt32(input, array);
array[0] ^= kw[0];
array[1] ^= kw[1];
array[2] ^= kw[2];
array[3] ^= kw[3];
camelliaF2(array, subkey, 0);
camelliaF2(array, subkey, 4);
camelliaF2(array, subkey, 8);
camelliaFLs(array, ke, 0);
camelliaF2(array, subkey, 12);
camelliaF2(array, subkey, 16);
camelliaF2(array, subkey, 20);
camelliaFLs(array, ke, 4);
camelliaF2(array, subkey, 24);
camelliaF2(array, subkey, 28);
camelliaF2(array, subkey, 32);
Pack.UInt32_To_BE(array[2] ^ kw[4], output);
Pack.UInt32_To_BE(array[3] ^ kw[5], output.Slice(4, output.Length - 4));
Pack.UInt32_To_BE(array[0] ^ kw[6], output.Slice(8, output.Length - 8));
Pack.UInt32_To_BE(array[1] ^ kw[7], output.Slice(12, output.Length - 12));
return 16;
}
private int ProcessBlock192or256(ReadOnlySpan<byte> input, Span<byte> output)
{
uint[] array = new uint[4];
Pack.BE_To_UInt32(input, array);
array[0] ^= kw[0];
array[1] ^= kw[1];
array[2] ^= kw[2];
array[3] ^= kw[3];
camelliaF2(array, subkey, 0);
camelliaF2(array, subkey, 4);
camelliaF2(array, subkey, 8);
camelliaFLs(array, ke, 0);
camelliaF2(array, subkey, 12);
camelliaF2(array, subkey, 16);
camelliaF2(array, subkey, 20);
camelliaFLs(array, ke, 4);
camelliaF2(array, subkey, 24);
camelliaF2(array, subkey, 28);
camelliaF2(array, subkey, 32);
camelliaFLs(array, ke, 8);
camelliaF2(array, subkey, 36);
camelliaF2(array, subkey, 40);
camelliaF2(array, subkey, 44);
Pack.UInt32_To_BE(array[2] ^ kw[4], output);
Pack.UInt32_To_BE(array[3] ^ kw[5], output.Slice(4, output.Length - 4));
Pack.UInt32_To_BE(array[0] ^ kw[6], output.Slice(8, output.Length - 8));
Pack.UInt32_To_BE(array[1] ^ kw[7], output.Slice(12, output.Length - 12));
return 16;
}
public CamelliaLightEngine()
{
initialised = false;
}
public virtual int GetBlockSize()
{
return 16;
}
public virtual void Init(bool forEncryption, ICipherParameters parameters)
{
if (!(parameters is KeyParameter))
throw new ArgumentException("only simple KeyParameter expected.");
setKey(forEncryption, ((KeyParameter)parameters).GetKey());
initialised = true;
}
public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
if (!initialised)
throw new InvalidOperationException("Camellia engine not initialised");
Check.DataLength(input, inOff, 16, "input buffer too short");
Check.OutputLength(output, outOff, 16, "output buffer too short");
if (_keyis128)
return ProcessBlock128(input.AsSpan(inOff), output.AsSpan(outOff));
return ProcessBlock192or256(input.AsSpan(inOff), output.AsSpan(outOff));
}
public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
if (!initialised)
throw new InvalidOperationException("Camellia engine not initialised");
Check.DataLength(input, 16, "input buffer too short");
Check.OutputLength(output, 16, "output buffer too short");
if (_keyis128)
return ProcessBlock128(input, output);
return ProcessBlock192or256(input, output);
}
}
}