Finalize OW password encryption and decryption.

This commit is contained in:
Ray Koopa 2019-01-02 16:29:18 +01:00
parent 1c401235d5
commit 61377411a1
5 changed files with 82 additions and 60 deletions

View File

@ -11,7 +11,7 @@ namespace Syroot.Worms.OnlineWorms.Launcher
public string UserName { get; set; }
public string Password { get; set; }
public int PasswordKey { get; set; }
public uint PasswordKey { get; set; }
public string ExecutablePath { get; set; } = "DWait.exe";
public string ExecutableArgs { get; set; }

View File

@ -1,16 +1,16 @@
{
// Server connection
"ServerIP": "127.0.0.1",
"ServerPort": "17022",
"ServerPort": 17022,
// Autologin credentials (optional)
"UserName": "UserName",
"Password": "Password",
// Debugging settings (optional)
"PasswordKey": "0", // base value to encrypt and decrypt password in launch file mapping
"PasswordKey": 1000, // base value to encrypt and decrypt password in launch file mapping
"ExecutablePath": "DWait.exe", // relative or absolute path to game executable
"ExecutableArgs": "", // additional arguments appended to command line
"MappingName": "KG1234567890", // launch file mapping identifier used and passed as first argument to game
"StartSuspended": "false" // true to create, but wait for confirmation to run the game process (to attach debuggers)
"StartSuspended": false // true to create, but wait for confirmation to run the game process (to attach debuggers)
}

View File

@ -11,7 +11,6 @@ namespace Syroot.Worms.Scratchpad
private static void Main(string[] args)
{
TestLaunchConfigEncryption();
}
private static void ConvertKsfImages()
@ -45,12 +44,6 @@ namespace Syroot.Worms.Scratchpad
}
}
private static void TestLaunchConfigEncryption()
{
LaunchConfig launchConfig = new LaunchConfig();
launchConfig.DecryptPassword("Unknown", 0);
}
private static void WriteMemoryManagerBlocks()
{
// Memory manager block allocation simulation.

View File

@ -12,10 +12,25 @@ namespace Syroot.Worms.OnlineWorms
/// </summary>
public class LaunchConfig
{
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const int _bufferSize = 64 * sizeof(uint);
private const string _passwordCharacters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// ---- FIELDS -------------------------------------------------------------------------------------------------
private static readonly Encoding _win949Encoding;
private static readonly char[] _invalidUserNameChars = { '=', '&', ' ' };
private static readonly byte[] _passwordDecryptShifts = new byte[]
{
0x12, 0x1D, 0x07, 0x19, 0x0F, 0x1F, 0x16, 0x1B, 0x09, 0x1A, 0x03, 0x0D, 0x13, 0x0E, 0x14, 0x0B,
0x05, 0x02, 0x17, 0x10, 0x0A, 0x18, 0x1C, 0x11, 0x06, 0x1E, 0x00, 0x15, 0x0C, 0x08, 0x04, 0x01
};
private static readonly byte[] _passwordEncryptShifts = new byte[]
{
0x1A, 0x1F, 0x11, 0x0A, 0x1E, 0x10, 0x18, 0x02, 0x1D, 0x08, 0x14, 0x0F, 0x1C, 0x0B, 0x0D, 0x04,
0x13, 0x17, 0x00, 0x0C, 0x0E, 0x1B, 0x06, 0x12, 0x15, 0x03, 0x09, 0x07, 0x16, 0x01, 0x19, 0x05
};
private string _userName;
private string _passwordString;
@ -100,8 +115,10 @@ namespace Syroot.Worms.OnlineWorms
/// </summary>
public string GetPassword()
{
// TODO: Decrypt password:
return _passwordString;
if (_passwordString == null)
return null;
string[] parts = _passwordString.Split(';');
return DecryptPassword(parts[1], UInt32.Parse(parts[2]));
}
/// <summary>
@ -110,71 +127,82 @@ namespace Syroot.Worms.OnlineWorms
/// </summary>
/// <param name="password">The password to store encrypted.</param>
/// <param name="key">The key to encrypt with.</param>
public void SetPassword(string password, int key)
public void SetPassword(string password, uint key = 1000)
{
// TODO: Encrypt password.
_passwordString = $";{password};{key}";
_passwordString = $";{EncryptPassword(password, key)};{key}";
}
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
public string DecryptPassword(string data, int key)
private string EncryptPassword(string password, uint key)
{
byte[] decodeBuffer = new byte[64];
int decodeIdx = 0;
int idx = 0;
do
using (MemoryStream inStream = new MemoryStream(new byte[_bufferSize]))
{
int subIdx = 0;
uint curValue = 0;
string dataRemain = data.Substring(idx);
do
// Write input into a buffer. Required to loop over the input password end.
inStream.WriteString(password, StringCoding.ZeroTerminated, Encoding.ASCII);
inStream.Position = 0;
using (MemoryStream outStream = new MemoryStream(new byte[_bufferSize]))
{
uint baseValue = 1;
if (subIdx > 0)
// Encrypt the contents character by character.
while (inStream.Position < password.Length)
{
int subIdxCounter = subIdx;
do
// Begin a new dword value at every 7th index.
uint dword = CryptDword(inStream.ReadUInt32() + key, true);
for (int j = 0; j < 7; j++)
{
baseValue *= 36;
subIdxCounter--;
} while (subIdxCounter != 0);
outStream.WriteByte((byte)_passwordCharacters[(int)(dword % _passwordCharacters.Length)]);
dword /= (uint)_passwordCharacters.Length;
}
}
char c = dataRemain[subIdx];
uint factor = (uint)(c < '0' || c > '9' ? c - '7' : c - '0');
curValue += baseValue * factor;
++subIdx;
} while (subIdx < 7);
idx += 7;
int value = DecryptValue(curValue, 0) - key;
Buffer.BlockCopy(BitConverter.GetBytes(value), 0, decodeBuffer, decodeIdx++, sizeof(int));
} while (idx < data.Length);
return Encoding.ASCII.GetString(decodeBuffer);
// Return the encrypted password as a zero-terminated ASCII string.
outStream.Position = 0;
return outStream.ReadString(StringCoding.ZeroTerminated, Encoding.ASCII);
}
}
}
public int DecryptValue(uint value, int shiftArrayIndex)
private string DecryptPassword(string encryptedPassword, uint key)
{
int[][] shifts = new int[][]
using (MemoryStream inStream = new MemoryStream(new byte[_bufferSize]))
{
new[] { 18, 29, 7, 25, 15, 31, 22, 27, 9, 26, 3, 13, 19, 14, 20, 11, 5, 2, 23, 16, 10, 24, 28, 17, 6, 30, 0, 21, 12, 8, 4, 1 },
new[] { 26, 31, 17, 10, 30, 16, 24, 2, 29, 8, 20, 15, 28, 11, 13, 4, 19, 23, 0, 12, 14, 27, 6, 18, 21, 3, 9, 22, 7, 1, 25, 5 }
};
int result = 0;
if (value != 0)
{
int idx = 0;
do
// Write input into a buffer. Required to loop over the input password end.
inStream.WriteString(encryptedPassword, StringCoding.Raw, Encoding.ASCII);
inStream.Position = 0;
using (MemoryStream outStream = new MemoryStream(new byte[_bufferSize]))
{
uint highBits = (uint)(value - (value & ~1));
value >>= 1; // / 2
if (highBits != 0)
// Decrypt the contents character by character.
for (int i = 0; i < encryptedPassword.Length; i += 7)
{
int shift = shifts[shiftArrayIndex][idx];
result += (int)(highBits << shift);
uint dword = 0;
for (int j = 0; j < 7; j++)
{
byte b = inStream.Read1Byte();
uint op1 = (uint)Math.Pow(_passwordCharacters.Length, j);
uint op2 = (uint)(b < '0' || b > '9' ? b - '7' : b - '0');
dword += op1 * op2;
}
// Finalize a new dword value at every 7th index.
outStream.WriteUInt32(CryptDword(dword, false) - key);
}
idx++;
} while (value != 0);
// Return the decrypted password as a zero-terminated ASCII string.
outStream.Position = 0;
return outStream.ReadString(StringCoding.ZeroTerminated, Encoding.ASCII);
}
}
}
private uint CryptDword(uint dword, bool encrypt)
{
// Shift least to most significant bits into the resulting value by an amount stored in a one-way array.
byte[] shifts = encrypt ? _passwordEncryptShifts : _passwordDecryptShifts;
uint result = 0;
int shiftIndex = 0;
while (dword > 0)
{
result += (dword & 1) << shifts[shiftIndex];
shiftIndex++;
dword >>= 1;
}
return result;
}

View File

@ -6,6 +6,7 @@
<Copyright>(c) Syroot, licensed under MIT</Copyright>
<Description>.NET library for loading and modifying files of Team17 Worms games.</Description>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>latest</LangVersion>
<PackageIconUrl>https://gitlab.com/Syroot/Worms/raw/master/res/icon.png</PackageIconUrl>
<PackageId>Syroot.Worms</PackageId>
<PackageLicenseUrl>https://gitlab.com/Syroot/Worms/raw/master/LICENSE</PackageLicenseUrl>