using System; using Org.BouncyCastle.Crypto.Parameters; 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 uint[] state = new uint[4]; private static readonly uint[] SIGMA = new uint[12] { 2694735487u, 1003262091u, 3061508184u, 1286239154u, 3337565999u, 3914302142u, 1426019237u, 4057165596u, 283453434u, 3731369245u, 2958461122u, 3018244605u }; 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, 255, 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"; public virtual bool IsPartialBlockOkay => false; 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 static uint bytes2uint(byte[] src, int offset) { uint num = 0u; for (int i = 0; i < 4; i++) { num = (num << 8) + src[i + offset]; } return num; } private static void uint2bytes(uint word, byte[] dst, int offset) { for (int i = 0; i < 4; i++) { dst[3 - i + offset] = (byte)word; word >>= 8; } } private byte lRot8(byte v, int rot) { return (byte)((v << rot) | (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)(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; uint[] array; (array = s)[2] = array[2] ^ (leftRotate(num4, 16) ^ num2); (array = s)[3] = array[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)(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; (array = s)[0] = array[0] ^ (leftRotate(num4, 16) ^ num2); (array = s)[1] = array[1] ^ leftRotate(num2, 8); } private void camelliaFLs(uint[] s, uint[] fkey, int keyoff) { uint[] array; (array = s)[1] = array[1] ^ leftRotate(s[0] & fkey[keyoff], 1); (array = s)[0] = array[0] ^ (fkey[1 + keyoff] | s[1]); (array = s)[2] = array[2] ^ (fkey[3 + keyoff] | s[3]); (array = s)[3] = array[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.Length) { case 16: _keyis128 = true; array[0] = bytes2uint(key, 0); array[1] = bytes2uint(key, 4); array[2] = bytes2uint(key, 8); array[3] = bytes2uint(key, 12); array[4] = (array[5] = (array[6] = (array[7] = 0u))); break; case 24: array[0] = bytes2uint(key, 0); array[1] = bytes2uint(key, 4); array[2] = bytes2uint(key, 8); array[3] = bytes2uint(key, 12); array[4] = bytes2uint(key, 16); array[5] = bytes2uint(key, 20); array[6] = ~array[4]; array[7] = ~array[5]; _keyis128 = false; break; case 32: array[0] = bytes2uint(key, 0); array[1] = bytes2uint(key, 4); array[2] = bytes2uint(key, 8); array[3] = bytes2uint(key, 12); array[4] = bytes2uint(key, 16); array[5] = bytes2uint(key, 20); array[6] = bytes2uint(key, 24); array[7] = bytes2uint(key, 28); _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++) { uint[] array6; uint[] array5 = (array6 = array2); int num = j; nint num2 = num; array5[num] = array6[num2] ^ 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); } return; } 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(byte[] input, int inOff, byte[] output, int outOff) { uint[] array2; for (int i = 0; i < 4; i++) { state[i] = bytes2uint(input, inOff + i * 4); uint[] array = (array2 = state); int num = i; nint num2 = num; array[num] = array2[num2] ^ kw[i]; } camelliaF2(state, subkey, 0); camelliaF2(state, subkey, 4); camelliaF2(state, subkey, 8); camelliaFLs(state, ke, 0); camelliaF2(state, subkey, 12); camelliaF2(state, subkey, 16); camelliaF2(state, subkey, 20); camelliaFLs(state, ke, 4); camelliaF2(state, subkey, 24); camelliaF2(state, subkey, 28); camelliaF2(state, subkey, 32); (array2 = state)[2] = array2[2] ^ kw[4]; (array2 = state)[3] = array2[3] ^ kw[5]; (array2 = state)[0] = array2[0] ^ kw[6]; (array2 = state)[1] = array2[1] ^ kw[7]; uint2bytes(state[2], output, outOff); uint2bytes(state[3], output, outOff + 4); uint2bytes(state[0], output, outOff + 8); uint2bytes(state[1], output, outOff + 12); return 16; } private int processBlock192or256(byte[] input, int inOff, byte[] output, int outOff) { uint[] array2; for (int i = 0; i < 4; i++) { state[i] = bytes2uint(input, inOff + i * 4); uint[] array = (array2 = state); int num = i; nint num2 = num; array[num] = array2[num2] ^ kw[i]; } camelliaF2(state, subkey, 0); camelliaF2(state, subkey, 4); camelliaF2(state, subkey, 8); camelliaFLs(state, ke, 0); camelliaF2(state, subkey, 12); camelliaF2(state, subkey, 16); camelliaF2(state, subkey, 20); camelliaFLs(state, ke, 4); camelliaF2(state, subkey, 24); camelliaF2(state, subkey, 28); camelliaF2(state, subkey, 32); camelliaFLs(state, ke, 8); camelliaF2(state, subkey, 36); camelliaF2(state, subkey, 40); camelliaF2(state, subkey, 44); (array2 = state)[2] = array2[2] ^ kw[4]; (array2 = state)[3] = array2[3] ^ kw[5]; (array2 = state)[0] = array2[0] ^ kw[6]; (array2 = state)[1] = array2[1] ^ kw[7]; uint2bytes(state[2], output, outOff); uint2bytes(state[3], output, outOff + 4); uint2bytes(state[0], output, outOff + 8); uint2bytes(state[1], output, outOff + 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, inOff, output, outOff); } return processBlock192or256(input, inOff, output, outOff); } public virtual void Reset() { } }