Created
July 27, 2022 15:00
-
-
Save barncastle/979c12a9c5e64d810a28ad1728e7e0f9 to your computer and use it in GitHub Desktop.
C# re-implementation of the WoW Matrix Card 2FA system available from TBC (2.0.0.5991)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class WoWMatrixCard | |
{ | |
public uint Width = 8; // SECURITYMATRIX_NUM_COLUMNS in SecurityMatrix.lua, default of 8 | |
public uint Height = 10; // SECURITYMATRIX_NUM_ROWS in SecurityMatrix.lua, default of 10 | |
public uint DigitCount = 2; // SECURITYMATRIX_NUM_MIN_DIGITS in SecurityMatrix.lua, default of 2 | |
public uint ChallengeCount; // number of cells/rounds to be completed | |
public ulong Seed; | |
public uint[] Coordinates; | |
private readonly byte[] _IPad = new byte[0x40]; | |
private readonly byte[] _OPad = new byte[0x40]; | |
private readonly SHA1 _SHA1 = SHA1.Create(); | |
private readonly ARC4 _ARC4 = new(); | |
private readonly byte[] _Buffer = new byte[1]; | |
/// <summary> | |
/// Matrix card initalisation method | |
/// <para>Called and populated from CmdAuthLogonChallenge</para> | |
/// </summary> | |
public void SetMatrixInfo(byte width, byte height, byte digitCount, byte challengeCount, ulong seed, byte[] sessionKey) | |
{ | |
Width = width; | |
Height = height; | |
DigitCount = digitCount; | |
ChallengeCount = challengeCount; | |
Seed = seed; | |
Coordinates = new uint[ChallengeCount]; | |
// generate the coordinates of the cells required | |
GenerateCoordinates(Seed); | |
// MD5(seed_bytes + sessionKey) | |
using MD5 md5 = MD5.Create(); | |
md5.TransformBlock(BitConverter.GetBytes(Seed), 0, 8, null, 0); | |
md5.TransformFinalBlock(sessionKey, 0, sessionKey.Length); | |
byte[] key = md5.Hash; | |
// initialise SARC4 | |
_ARC4.SetKey(key); | |
// HMAC | |
Array.Fill<byte>(_IPad, 0x36); | |
Array.Fill<byte>(_OPad, 0x5C); | |
for (int i = 0; i < key.Length; i++) | |
{ | |
_IPad[i] ^= key[i]; | |
_OPad[i] ^= key[i]; | |
} | |
// initialise the input hash | |
_SHA1.Initialize(); | |
_SHA1.TransformBlock(_IPad, 0, _IPad.Length, null, 0); | |
} | |
/// <summary> | |
/// Invoked by the UI to ascertain which cell to highlight each round | |
/// </summary> | |
public bool GetMatrixCoordinates(uint round, out uint x, out uint y) | |
{ | |
x = y = 0; | |
if (round >= ChallengeCount) | |
return false; | |
uint coord = Coordinates[round]; | |
x = coord % Width; | |
y = coord / Width; | |
if (y >= Height) | |
return false; | |
return true; | |
} | |
/// <summary> | |
/// Invoked by SecurityMatrix.lua each time a digit is selected | |
/// </summary> | |
public void EnterMatrix(byte value) | |
{ | |
_Buffer[0] = value; | |
// ARC4 encrypt our input and | |
// append the output to our SHA hash | |
_ARC4.Process(_Buffer, 1); | |
_SHA1.TransformBlock(_Buffer, 0, 1, null, 0); | |
} | |
/// <summary> | |
/// Invoked by SecurityMatrix.lua once all digits have been | |
/// selected and OK is pressed | |
/// </summary> | |
public byte[] FinalizeMatrix() | |
{ | |
// finalise the input hash | |
_SHA1.TransformFinalBlock(_Buffer, 0, 0); | |
byte[] input_hash = _SHA1.Hash; | |
// compute our proof | |
_SHA1.Initialize(); | |
_SHA1.TransformBlock(_OPad, 0, _OPad.Length, null, 0); | |
_SHA1.TransformFinalBlock(input_hash, 0, input_hash.Length); | |
return _SHA1.Hash; | |
} | |
/// <summary> | |
/// Calculates which cell will be requested at each round | |
/// </summary> | |
private void GenerateCoordinates(ulong seed) | |
{ | |
uint matrixSize = Width * Height; | |
uint[] matrixIndicies = new uint[matrixSize]; | |
// populate the indicies | |
for (uint i = 1u; i < matrixSize; i++) | |
matrixIndicies[i] = i; | |
for (uint i = 0u; i < ChallengeCount; i++) | |
{ | |
uint count = matrixSize - i; | |
uint index = (uint)(seed % count); | |
Coordinates[i] = matrixIndicies[index]; | |
// "pop" the selected entry to prevent re-use | |
// e.g. i(1) : [0, 1, 2] => [0, 2, 2] | |
for (uint j = index; j < count - 1; j++) | |
matrixIndicies[j] = matrixIndicies[j + 1]; | |
seed /= count; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment