diff --git a/Arrowgene.Ddon.Cli/Command/BruteForceCommand.cs b/Arrowgene.Ddon.Cli/Command/BruteForceCommand.cs index 19251d5f3..e98f903ef 100644 --- a/Arrowgene.Ddon.Cli/Command/BruteForceCommand.cs +++ b/Arrowgene.Ddon.Cli/Command/BruteForceCommand.cs @@ -69,16 +69,15 @@ private void ProcessMs(uint ms, int depth) } Camellia c = new Camellia(); - byte[][] subKey = new byte[34][]; byte[] output = new byte[BlockSize]; for (int j = 0; j < keyBuffer.Length - KeyLength; j++) { - c.KeySchedule(KeyLengthBits, keyBuffer.Slice(j, KeyLength), subKey); + c.KeySchedule( keyBuffer.Slice(j, KeyLength), out Memory subKey); c.CryptBlock( true, - KeyLength, + KeyLength * 8, Data, - subKey, + subKey.Span, output ); for (int i = 0; i < BlockSize; i++) diff --git a/Arrowgene.Ddon.Server/Network/PacketFactory.cs b/Arrowgene.Ddon.Server/Network/PacketFactory.cs index 81f621c6c..617d1d059 100644 --- a/Arrowgene.Ddon.Server/Network/PacketFactory.cs +++ b/Arrowgene.Ddon.Server/Network/PacketFactory.cs @@ -12,7 +12,7 @@ namespace Arrowgene.Ddon.Server.Network public class PacketFactory { private static readonly DdonLogger Logger = LogProvider.Logger(typeof(PacketFactory)); - private static readonly Camellia _camellia = new Camellia(); + private static readonly Camellia Camellia = new Camellia(); private static readonly byte[] CamelliaKey = new byte[] { @@ -28,6 +28,7 @@ public class PacketFactory private const int PacketLengthFieldSize = 2; private const int PacketHeaderSize = 9; private const int PacketMinimumDataSize = 16; + private const uint CamelliaKeyLength = 32 * 8; private bool _readHeader; private ushort _dataSize; @@ -37,20 +38,26 @@ public class PacketFactory private readonly ServerSetting _setting; private byte[] _camelliaKey; private IPacketIdResolver _packetIdResolver; + private Memory _t8Encrypt; + private Memory _t8Decrypt; + private Memory _camelliaSubKey; public PacketFactory(ServerSetting setting, IPacketIdResolver packetIdResolver) { _setting = setting; - _camelliaKey = CamelliaKey; _packetCount = 0; _packetIdResolver = packetIdResolver; + _t8Encrypt = new Memory(new byte[8]); + _t8Decrypt = new Memory(new byte[8]); + SetCamelliaKey(CamelliaKey); Reset(); } public void SetCamelliaKey(byte[] camelliaKey) { _camelliaKey = Copy(camelliaKey); + Camellia.KeySchedule(_camelliaKey, out _camelliaSubKey); } public byte[] WriteWithoutHeader(Packet packet) @@ -135,6 +142,7 @@ public List Read(byte[] data) Reset(); return packets; } + _readHeader = true; } @@ -142,7 +150,7 @@ public List Read(byte[] data) { byte[] encryptedPacketData = _buffer.ReadBytes(_dataSize); byte[] packetData = Decrypt(encryptedPacketData); - + IBuffer packetBuffer = new StreamBuffer(packetData); packetBuffer.SetPositionStart(); byte groupId = packetBuffer.ReadByte(); @@ -203,13 +211,27 @@ public List Read(byte[] data) public byte[] Encrypt(byte[] data) { - _camellia.Encrypt(data, out Span encrypted, _camelliaKey, Copy(CamelliaIv)); + Camellia.Encrypt( + data, + out Span encrypted, + CamelliaKeyLength, + _camelliaSubKey.Span, + Copy(CamelliaIv), + _t8Encrypt.Span + ); return encrypted.ToArray(); } public byte[] Decrypt(byte[] encrypted) { - _camellia.Decrypt(encrypted, out Span decrypted, _camelliaKey, Copy(CamelliaIv)); + Camellia.Decrypt( + encrypted, + out Span decrypted, + CamelliaKeyLength, + _camelliaSubKey.Span, + Copy(CamelliaIv), + _t8Decrypt.Span + ); return decrypted.ToArray(); } diff --git a/Arrowgene.Ddon.Shared/Crypto/Camellia.cs b/Arrowgene.Ddon.Shared/Crypto/Camellia.cs index a0359433d..9e189f4cd 100644 --- a/Arrowgene.Ddon.Shared/Crypto/Camellia.cs +++ b/Arrowgene.Ddon.Shared/Crypto/Camellia.cs @@ -4,32 +4,41 @@ namespace Arrowgene.Ddon.Shared.Crypto { public class Camellia { - /// - /// Dragons Dogma Online Network Encryption - /// - public void Encrypt(Span input, out Span output, byte[] key, Span prv) + public uint GetKeyLength(Span key) + { + return (uint) key.Length * 8; + } + + public void Encrypt(Span input, out Span output, Span key, Span prv, bool pad = false) + { + KeySchedule(key, out Memory subKey); + Encrypt(input, out output, GetKeyLength(key), subKey.Span, prv, new byte[8], pad); + } + + public void Decrypt(Span input, out Span output, Span key, Span prv, bool pad = false) + { + KeySchedule(key, out Memory subKey); + Decrypt(input, out output, GetKeyLength(key), subKey.Span, prv, new byte[8], pad); + } + + public void Encrypt(Span input, out Span output, uint keyLength, Span subKey, Span prv, Span t8, bool pad = false) { - // output_size = input_size + (block_size - (input_size % block_size)) int mod = input.Length % 16; if (mod > 0) { + if (!pad) + { + throw new Exception("Invalid PlainText Size"); + } + int padding = 16 - mod; byte[] padInput = new byte[input.Length + padding]; - Buffer.BlockCopy(input.ToArray(), 0, padInput,0, input.Length); + Buffer.BlockCopy(input.ToArray(), 0, padInput, 0, input.Length); input = padInput; } - - - // TODO - Modifies input value to apply XOR - make a copy - // TODO check if input length is dividable by 16 - uint keyLength = (uint) key.Length * 8; - byte[][] subkey = new byte[34][]; - - KeySchedule(keyLength, key, subkey); int length = input.Length; output = new byte[length]; - int current = 0; while (current < length) { @@ -38,89 +47,42 @@ public void Encrypt(Span input, out Span output, byte[] key, Span input, Span output, byte[] key, Span prv) - { - // TODO - Modifies input value to apply XOR - make a copy - // TODO check if input length is dividable by 16 - uint keyLength = (uint) key.Length * 8; - byte[][] subkey = new byte[34][]; - int idx = 0; - for (int i = 0; i < subkey.Length; i++) - { - for (int j = 0; j < subkey[i].Length; i++) - { - subkey[i][j] = key[idx]; - idx++; - } - } - - int length = input.Length; - if (output.Length < input.Length) - { - // error not enough space - } - int current = 0; - while (current < length) - { - int xorLen = current + 16 < length ? 16 : length - current; - for (int i = 0; i < xorLen; i++) - { - input[current + i] = (byte) (input[current + i] ^ prv[i]); - } CryptBlock( false, keyLength, input.Slice(current, 16), - subkey, - output.Slice(current, 16) + subKey, + output.Slice(current, 16), + t8 ); for (int i = 0; i < xorLen; i++) { prv[i] = output[current + i]; } + current += xorLen; } } - - /// - /// Dragons Dogma Online Network Decryption - /// - public void Decrypt(Span input, out Span output, byte[] key, Span prv) + public void Decrypt(Span input, out Span output, uint keyLength, Span subKey, Span prv, Span t8, bool pad = false) { - // TODO check if input length is dividable by 16 int mod = input.Length % 16; if (mod > 0) { + if (!pad) + { + throw new Exception("Invalid CipherText Size"); + } + int padding = 16 - mod; byte[] padInput = new byte[input.Length + padding]; - Buffer.BlockCopy(input.ToArray(), 0, padInput,0, input.Length); + Buffer.BlockCopy(input.ToArray(), 0, padInput, 0, input.Length); input = padInput; } - - uint keyLength = (uint) key.Length * 8; - byte[][] subkey = new byte[34][]; - KeySchedule(keyLength, key, subkey); int length = input.Length; output = new byte[length]; - int current = 0; while (current < length) { @@ -128,8 +90,9 @@ public void Decrypt(Span input, out Span output, byte[] key, Span input, out Span output, byte[] key, Span key, out Memory subKey) + { + KeySchedule(key, out subKey, new byte[8]); + } + /* * Camellia key schedule * subkey[26] should be allocated for keyLen == 128. * otherwise subkey[34] should be allocated. */ - public void KeySchedule(uint keyLen, Span key, byte[][] subkey) + public void KeySchedule(Span key, out Memory subKey, Span t8) { + uint keyLen = GetKeyLength(key); + /* 0...KL, 1...KR, 2...KA, 3...KB */ byte[][] ikey = new byte[4][] { @@ -162,13 +132,6 @@ public void KeySchedule(uint keyLen, Span key, byte[][] subkey) new byte[16], }; - // subkey - // todo calculate and initialize subkey size - for (int subKeyIndex = 0; subKeyIndex < subkey.Length; subKeyIndex++) - { - subkey[subKeyIndex] = new byte[8]; - } - Span pl; Span pr; Span p; @@ -192,12 +155,14 @@ public void KeySchedule(uint keyLen, Span key, byte[][] subkey) /* padding */ int bytes = (int) keyLen / 8; + int rounds = bytes / 16; for (int round = 0; round < rounds; round++) { int currentBytes = round * 16; int remainingBytes = bytes - currentBytes; int count = remainingBytes > 16 ? 16 : remainingBytes; + // TODO Buffer.BlockCopy(key.ToArray(), round * 16, ikey[round], 0, count); } @@ -221,7 +186,7 @@ public void KeySchedule(uint keyLen, Span key, byte[][] subkey) xorOctets(16, ikey[i / 2 + 1], ikey[0], ikey[2]); } - CamelliaRound(Sigma[i], pl, pr); + CamelliaRound(Sigma[i], pl, pr, t8); p = pl; pl = pr; pr = p; @@ -233,14 +198,16 @@ public void KeySchedule(uint keyLen, Span key, byte[][] subkey) Span spanKey3 = new Span(ikey[3]); Span spanKey3_0 = spanKey3.Slice(0, 8); Span spanKey3_8 = spanKey3.Slice(8, 8); - CamelliaRound(Sigma[4], spanKey3_0, spanKey3_8); - CamelliaRound(Sigma[5], spanKey3_8, spanKey3_0); + CamelliaRound(Sigma[4], spanKey3_0, spanKey3_8, t8); + CamelliaRound(Sigma[5], spanKey3_8, spanKey3_0, t8); } /* subkey generation */ aki = dki = ski = 0; + int subKeyCount; if (keyLen == 128) { + subKeyCount = 26; maxikey = 2; drop = drop128; // memcpy(ikey[1], ikey[2], 16); /* ikey[1] is KA for 128-bit key */ @@ -249,22 +216,23 @@ public void KeySchedule(uint keyLen, Span key, byte[][] subkey) else { /* keyLen == 192 or 256 */ + subKeyCount = 34; maxikey = 4; drop = drop256; } + subKey = new byte[subKeyCount * 8]; + for (i = 0; i < 8; i++) { for (j = 0; j < 2 * maxikey; j++) { if (aki != drop[dki]) { - // memcpy(subkey[ski++], &ikey[j / 2][(j % 2) * 8], 8); byte[] iKeySrc = ikey[j / 2]; Span iKeySrcSpan = new Span(iKeySrc); Span src = iKeySrcSpan.Slice((j % 2) * 8, 8); - // todo optimize copy - Buffer.BlockCopy(src.ToArray(), 0, subkey[ski], 0, 8); + src.CopyTo(subKey.Span.Slice(ski * 8)); ski++; } else @@ -289,7 +257,21 @@ public void KeySchedule(uint keyLen, Span key, byte[][] subkey) } } - public void CryptBlock(bool decrypt, uint keyLen, Span pt, byte[][] subkey, Span ct) + public void CryptBlock(bool decrypt, uint keyLen, Span pt, Span subKey, Span ct) + { + CryptBlock(decrypt, keyLen, pt, subKey, ct, new byte[8]); + } + + /// + /// Crypts 16byte block + /// + /// + /// + /// + /// + /// + /// temporary 8byte array to use + public void CryptBlock(bool decrypt, uint keyLen, Span pt, Span subKey, Span ct, Span t8) { int r; /* round */ int ski; /* subkey index */ @@ -309,10 +291,7 @@ public void CryptBlock(bool decrypt, uint keyLen, Span pt, byte[][] subkey } /* prewhitening */ - //xorOctets(16, pt, subkey[ski], ct); - xorOctets(8, pt.Slice(0, 8), subkey[ski], ct.Slice(0, 8)); - xorOctets(8, pt.Slice(8, 8), subkey[ski + 1], ct.Slice(8, 8)); - + xorOctets(16, pt.Slice(0, 16), subKey.Slice(ski * 8, 16), ct.Slice(0, 16)); if (decrypt) { /* decryption */ @@ -334,15 +313,15 @@ public void CryptBlock(bool decrypt, uint keyLen, Span pt, byte[][] subkey if (r == 6 || r == 12 || r == 18) { - CamelliaFL(subkey[ski], ct.Slice(0, 8)); + CamelliaFL(subKey.Slice(ski * 8, 8), ct.Slice(0, 8), t8); ski += direction; - CamelliaFLinv(subkey[ski], ct.Slice(8, 8)); + CamelliaFLinv(subKey.Slice(ski * 8, 8), ct.Slice(8, 8), t8); ski += direction; } - CamelliaRound(subkey[ski], ct.Slice(0, 8), ct.Slice(8, 8)); + CamelliaRound(subKey.Slice(ski * 8, 8), ct.Slice(0, 8), ct.Slice(8, 8), t8); ski += direction; - CamelliaRound(subkey[ski], ct.Slice(8, 8), ct.Slice(0, 8)); + CamelliaRound(subKey.Slice(ski * 8, 8), ct.Slice(8, 8), ct.Slice(0, 8), t8); ski += direction; } @@ -354,30 +333,27 @@ public void CryptBlock(bool decrypt, uint keyLen, Span pt, byte[][] subkey ski--; } - //xorOctets(16, ct, subkey[ski], ct); - - xorOctets(8, ct.Slice(0, 8), subkey[ski], ct.Slice(0, 8)); - xorOctets(8, ct.Slice(8, 8), subkey[ski + 1], ct.Slice(8, 8)); + xorOctets(16, ct.Slice(0, 16), subKey.Slice(ski * 8, 16), ct.Slice(0, 16)); } private byte s1(int x) { - return s[x]; + return S[x]; } private byte s2(int x) { - return (byte) ((s[x] << 1) + (s[x] >> 7)); + return (byte) ((S[x] << 1) + (S[x] >> 7)); } private byte s3(int x) { - return (byte) ((s[x] << 7) + (s[x] >> 1)); + return (byte) ((S[x] << 7) + (S[x] >> 1)); } private byte s4(int x) { - return s[(byte) (x << 1) + (x >> 7)]; + return S[(byte) (x << 1) + (x >> 7)]; } /* dst[] <- src1[] ^ src2[] */ @@ -478,66 +454,62 @@ private void rot17(Span x) } /* Camellia round function without swap */ - private void CamelliaRound(byte[] subkey, Span l, Span r) + private void CamelliaRound(Span subKey, Span l, Span r, Span t8) { - byte[] t = new byte[8]; - /* key XOR */ - xorOctets(8, subkey, l, t); + xorOctets(8, subKey, l, t8); /* S-Function */ - t[0] = s1(t[0]); - t[1] = s2(t[1]); - t[2] = s3(t[2]); - t[3] = s4(t[3]); - t[4] = s2(t[4]); - t[5] = s3(t[5]); - t[6] = s4(t[6]); - t[7] = s1(t[7]); + t8[0] = s1(t8[0]); + t8[1] = s2(t8[1]); + t8[2] = s3(t8[2]); + t8[3] = s4(t8[3]); + t8[4] = s2(t8[4]); + t8[5] = s3(t8[5]); + t8[6] = s4(t8[6]); + t8[7] = s1(t8[7]); /* P-Function with Feistel XOR */ - byte a = (byte) (t[0] ^ t[3] ^ t[4] ^ t[5] ^ t[6]); + byte a = (byte) (t8[0] ^ t8[3] ^ t8[4] ^ t8[5] ^ t8[6]); r[7] ^= a; - a ^= (byte) (t[0] ^ t[1] ^ t[2]); + a ^= (byte) (t8[0] ^ t8[1] ^ t8[2]); r[3] ^= a; - a ^= (byte) (t[1] ^ t[6] ^ t[7]); + a ^= (byte) (t8[1] ^ t8[6] ^ t8[7]); r[6] ^= a; - a ^= (byte) (t[0] ^ t[1] ^ t[3]); + a ^= (byte) (t8[0] ^ t8[1] ^ t8[3]); r[2] ^= a; - a ^= (byte) (t[0] ^ t[5] ^ t[6]); + a ^= (byte) (t8[0] ^ t8[5] ^ t8[6]); r[5] ^= a; - a ^= (byte) (t[0] ^ t[2] ^ t[3]); + a ^= (byte) (t8[0] ^ t8[2] ^ t8[3]); r[1] ^= a; - a ^= (byte) (t[3] ^ t[4] ^ t[5]); + a ^= (byte) (t8[3] ^ t8[4] ^ t8[5]); r[4] ^= a; - a ^= (byte) (t[1] ^ t[2] ^ t[3]); + a ^= (byte) (t8[1] ^ t8[2] ^ t8[3]); r[0] ^= a; } /* Camellia FL function */ - private void CamelliaFL(Span subkey, Span x) + private void CamelliaFL(Span subKey, Span x, Span t4) { - byte[] t = new byte[4]; - and4octets(x.Slice(0, 4), subkey.Slice(0, 4), t); - rot1(4, t); - xorOctets(4, t, x.Slice(4, 4), x.Slice(4, 4)); - or4octets(x.Slice(4, 4), subkey.Slice(4, 4), t); - xorOctets(4, x.Slice(0, 4), t, x.Slice(0, 4)); + and4octets(x.Slice(0, 4), subKey.Slice(0, 4), t4); + rot1(4, t4); + xorOctets(4, t4, x.Slice(4, 4), x.Slice(4, 4)); + or4octets(x.Slice(4, 4), subKey.Slice(4, 4), t4); + xorOctets(4, x.Slice(0, 4), t4, x.Slice(0, 4)); } /* Camellia FL^{-1} function */ - private void CamelliaFLinv(Span subkey, Span y) + private void CamelliaFLinv(Span subKey, Span y, Span t4) { - byte[] t = new byte[4]; - or4octets(y.Slice(4, 4), subkey.Slice(4, 4), t); - xorOctets(4, y.Slice(0, 4), t, y.Slice(0, 4)); - and4octets(y.Slice(0, 4), subkey.Slice(0, 4), t); - rot1(4, t); - xorOctets(4, t, y.Slice(4, 4), y.Slice(4, 4)); + or4octets(y.Slice(4, 4), subKey.Slice(4, 4), t4); + xorOctets(4, y.Slice(0, 4), t4, y.Slice(0, 4)); + and4octets(y.Slice(0, 4), subKey.Slice(0, 4), t4); + rot1(4, t4); + xorOctets(4, t4, y.Slice(4, 4), y.Slice(4, 4)); } /* sbox */ - private static byte[] s = new byte[256] + private static readonly byte[] S = new byte[256] { 0x70, 0x82, 0x2c, 0xec, 0xb3, 0x27, 0xc0, 0xe5, 0xe4, 0x85, 0x57, 0x35, 0xea, 0x0c, 0xae, 0x41, @@ -574,7 +546,7 @@ private void CamelliaFLinv(Span subkey, Span y) }; /* key schedule constants */ - private static byte[][] Sigma = new byte[6][] + private static readonly byte[][] Sigma = new byte[6][] { new byte[8] {0xa0, 0x9e, 0x66, 0x7f, 0x3b, 0xcc, 0x90, 0x8b}, new byte[8] {0xb6, 0x7a, 0xe8, 0x58, 0x4c, 0xaa, 0x73, 0xb2}, diff --git a/Arrowgene.Ddon.Test/Arrowgene.Ddon.Test.csproj b/Arrowgene.Ddon.Test/Arrowgene.Ddon.Test.csproj index 94edc4f59..9ebf55e4a 100644 --- a/Arrowgene.Ddon.Test/Arrowgene.Ddon.Test.csproj +++ b/Arrowgene.Ddon.Test/Arrowgene.Ddon.Test.csproj @@ -10,6 +10,7 @@ false + all diff --git a/Arrowgene.Ddon.Test/Shared/Crypto/CamelliaTest.cs b/Arrowgene.Ddon.Test/Shared/Crypto/CamelliaTest.cs new file mode 100644 index 000000000..caf36f63d --- /dev/null +++ b/Arrowgene.Ddon.Test/Shared/Crypto/CamelliaTest.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using System.Text; +using Arrowgene.Ddon.Shared.Crypto; +using Xunit; + +namespace Arrowgene.Ddon.Test.Shared.Crypto +{ + public class CamelliaTest + { + [Fact] + public void TestCamellia() + { + Camellia c = new Camellia(); + byte[] input = new byte[1024]; + byte[] key = Encoding.UTF8.GetBytes("0123456789012345"); + byte[] iv = Encoding.UTF8.GetBytes("5432109876543210"); + c.Encrypt(input.AsSpan().ToArray(), out Span encrypted, key, iv); + iv = Encoding.UTF8.GetBytes("5432109876543210"); + c.Decrypt(encrypted, out Span decrypted, key, iv); + + Assert.True(StructuralComparisons.StructuralEqualityComparer.Equals(input, decrypted.ToArray())); + } + + [Fact] + public void TestCamelliaSubKey() + { + Camellia c = new Camellia(); + byte[] input = new byte[1024]; + byte[] key = Encoding.UTF8.GetBytes("0123456789012345"); + byte[] iv = Encoding.UTF8.GetBytes("5432109876543210"); + uint keyLength = c.GetKeyLength(key); + Span t8 = new byte[8]; + c.KeySchedule(key, out Memory subKey, t8); + c.Encrypt(input.AsSpan().ToArray(), out Span encrypted, keyLength, subKey.Span, iv, t8); + iv = Encoding.UTF8.GetBytes("5432109876543210"); + c.Decrypt(encrypted, out Span decrypted, keyLength, subKey.Span, iv, t8); + Assert.True(StructuralComparisons.StructuralEqualityComparer.Equals(input, decrypted.ToArray())); + } + } +} diff --git a/Arrowgene.Ddon.Test/Shared/Entity/Structure/C2LCreateCharacterDataReqTest.cs b/Arrowgene.Ddon.Test/Shared/Entity/Structure/C2LCreateCharacterDataReqTest.cs index b5c52cc29..1009154c5 100644 --- a/Arrowgene.Ddon.Test/Shared/Entity/Structure/C2LCreateCharacterDataReqTest.cs +++ b/Arrowgene.Ddon.Test/Shared/Entity/Structure/C2LCreateCharacterDataReqTest.cs @@ -26,8 +26,6 @@ public void TestC2LCreateCharacterDataReqTestSerializer() EntitySerializer.Get().Write(buffer, deserialized); byte[] serialized = buffer.GetAllBytes(); Assert.True(StructuralComparisons.StructuralEqualityComparer.Equals(bin, serialized)); - - //C2LCreateCharacterDataReq character = new C2LCreateCharacterDataReq(); } } diff --git a/Arrowgene.Ddon.Test/Shared/Entity/Structure/CDataCharacterListInfoTest.cs b/Arrowgene.Ddon.Test/Shared/Entity/Structure/CDataCharacterListInfoTest.cs index b88e2c513..ece70ee41 100644 --- a/Arrowgene.Ddon.Test/Shared/Entity/Structure/CDataCharacterListInfoTest.cs +++ b/Arrowgene.Ddon.Test/Shared/Entity/Structure/CDataCharacterListInfoTest.cs @@ -25,10 +25,6 @@ public void TestCDataCharacterListInfoSerializer() EntitySerializer.Get().WriteList(buffer, deserialized); byte[] serialized = buffer.GetAllBytes(); Assert.True(StructuralComparisons.StructuralEqualityComparer.Equals(bin, serialized)); - - CDataCharacterListInfo character = new CDataCharacterListInfo(); - - int i = 1; } } }